/*******************************************************************************
* KindEditor - WYSIWYG HTML Editor for Internet
* Copyright (C) 2006-2011 Longhao Luo
*
* @author Roddy <luolonghao@gmail.com>
* @site http://www.kindsoft.net/
* @licence http://www.kindsoft.net/license.php
* @version 3.5.5 (2011-05-22)
*******************************************************************************/

(function (undefined) {

    if (window.KindEditor !== undefined) return;

    var KE = {};

    KE.version = '3.5.5 (2011-05-22)';

    KE.scriptPath = (function () {
        var elements = document.getElementsByTagName('script');
        for (var i = 0, len = elements.length; i < len; i++) {
            var src = elements[i].src;
            if (src && src.match(/kindeditor[\w\-\.]*\.js/)) {
                return src.substring(0, src.lastIndexOf('/') + 1);
            }
        }
        return '';
    })();

    KE.browser = (function () {
        var ua = navigator.userAgent.toLowerCase();
        return {
            VERSION: ua.match(/(msie|firefox|webkit|opera)[\/:\s](\d+)/) ? RegExp.$2 : '0',
            IE: (ua.indexOf('msie') > -1 && ua.indexOf('opera') == -1),
            GECKO: (ua.indexOf('gecko') > -1 && ua.indexOf('khtml') == -1),
            WEBKIT: (ua.indexOf('applewebkit') > -1),
            OPERA: (ua.indexOf('opera') > -1)
        };
    })();

    KE.setting = {
        wyswygMode: true,
        loadStyleMode: true,
        resizeMode: 2,
        filterMode: false,
        autoSetDataMode: false,
        shadowMode: true,
        useContextmenu: true,
        urlType: '',
        skinType: 'default',
        syncType: 'form',
        newlineTag: 'p',
        dialogAlignType: 'page',
        cssPath: '',
        skinsPath: KE.scriptPath + 'skins/',
        pluginsPath: KE.scriptPath + 'plugins/',
        minWidth: 200,
        minHeight: 100,
        minChangeSize: 5,
        toolbarLineHeight: 24,
        statusbarHeight: 11,
        items: [
		'source', '|', 'fullscreen', 'undo', 'redo', 'print', 'cut', 'copy', 'paste',
		'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
		'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
		'superscript', '|', 'selectall', '-',
		'title', 'fontname', 'fontsize', '|', 'textcolor', 'bgcolor', 'bold',
		'italic', 'underline', 'strikethrough', 'removeformat', '|', 'image',
		'flash', 'media', 'advtable', 'hr', 'emoticons', 'link', 'unlink', '|', 'about'
	],
        colorTable: [
		['#E53333', '#E56600', '#FF9900', '#64451D', '#DFC5A4', '#FFE500'],
		['#009900', '#006600', '#99BB00', '#B8D100', '#60D978', '#00D5FF'],
		['#337FE5', '#003399', '#4C33E5', '#9933E5', '#CC33E5', '#EE33EE'],
		['#FFFFFF', '#CCCCCC', '#999999', '#666666', '#333333', '#000000']
	],
        noEndTags: ['br', 'hr', 'img', 'area', 'col', 'embed', 'input', 'param'],
        inlineTags: ['b', 'del', 'em', 'font', 'i', 'span', 'strike', 'strong', 'sub', 'sup', 'u'],
        endlineTags: [
		'br', 'hr', 'table', 'tbody', 'td', 'tr', 'th', 'div', 'p', 'ol', 'ul',
		'li', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'script', 'style', 'marquee'
	],
        htmlTags: {
            font: ['color', 'size', 'face', '.background-color'],
            span: [
			'.color', '.background-color', '.font-size', '.font-family', '.background',
			'.font-weight', '.font-style', '.text-decoration', '.vertical-align'
		],
            div: [
			'align', '.border', '.margin', '.padding', '.text-align', '.color',
			'.background-color', '.font-size', '.font-family', '.font-weight', '.background',
			'.font-style', '.text-decoration', '.vertical-align', '.margin-left'
		],
            table: [
			'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'bordercolor',
			'.padding', '.margin', '.border', 'bgcolor', '.text-align', '.color', '.background-color',
			'.font-size', '.font-family', '.font-weight', '.font-style', '.text-decoration', '.background',
			'.width', '.height'
		],
            'td,th': [
			'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor',
			'.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight',
			'.font-style', '.text-decoration', '.vertical-align', '.background'
		],
            a: ['href', 'target', 'name'],
            embed: ['src', 'width', 'height', 'type', 'loop', 'autostart', 'quality', '.width', '.height', 'align', 'allowscriptaccess', '/'],
            img: ['src', 'width', 'height', 'border', 'alt', 'title', '.width', '.height', '/'],
            hr: ['/'],
            br: ['/'],
            'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6': [
			'align', '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.background',
			'.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.text-indent', '.margin-left'
		],
            'tbody,tr,strong,b,sub,sup,em,i,u,strike': []
        },
        mediaTypes: {
            rm: 'audio/x-pn-realaudio-plugin',
            flash: 'application/x-shockwave-flash',
            media: 'video/x-ms-asf-plugin'
        },
        afterTab: function (id) {
            KE.util.setSelection(id);
            KE.util.insertHtml(id, '&nbsp;&nbsp;&nbsp;&nbsp;');
        }
    };

    KE.g = {};

    KE.plugin = {};

    KE.$ = function (id, doc) {
        var doc = doc || document;
        return doc.getElementById(id);
    };

    KE.$$ = function (name, doc) {
        var doc = doc || document;
        return doc.createElement(name);
    };

    KE.event = {
        add: function (el, type, fn, id) {
            if (el.addEventListener) {
                el.addEventListener(type, fn, false);
            } else if (el.attachEvent) {
                el.attachEvent('on' + type, fn);
            }
            if (id !== undefined) {
                KE.g[id].eventStack.push({
                    el: el,
                    type: type,
                    fn: fn
                });
            }
        },
        remove: function (el, type, fn, id) {
            if (el.removeEventListener) {
                el.removeEventListener(type, fn, false);
            } else if (el.detachEvent) {
                el.detachEvent('on' + type, fn);
            }
            if (id !== undefined) {
                var stack = KE.g[id].eventStack;
                for (var i = 0, len = stack.length; i < len; i++) {
                    var item = stack[i];
                    if (item && el === item.el && type === item.type && fn === item.fn) {
                        delete stack[i];
                    }
                }
            }
        },
        stopPropagation: function (e) {
            if (e.stopPropagation) e.stopPropagation();
            if (e.cancelBubble !== undefined) e.cancelBubble = true;
        },
        preventDefault: function (e) {
            if (e.preventDefault) e.preventDefault();
            if (e.returnValue !== undefined) e.returnValue = false;
        },
        stop: function (e) {
            this.stopPropagation(e);
            this.preventDefault(e);
        },
        bind: function (el, type, fn, id) {
            this.add(el, type, function (e) {
                fn(e);
                KE.event.stop(e);
                return false;
            }, id);
        },
        input: function (el, func, id) {
            this.add(el, 'keyup', function (e) {
                if (!e.ctrlKey && !e.altKey && (e.keyCode < 16 || e.keyCode > 18) && e.keyCode != 116) {
                    func(e);
                    KE.event.stop(e);
                    return false;
                }
            }, id);
            function handler(e) {
                window.setTimeout(function () {
                    func(e);
                }, 1);
            }
            var newElement = (el.nodeName == '#document') ? el.body : el;
            this.add(newElement, 'paste', handler, id);
            this.add(newElement, 'cut', handler, id);
        },
        ctrl: function (el, key, func, id) {
            key = key.toString().match(/^\d{2,}$/) ? key : key.toUpperCase().charCodeAt(0);
            this.add(el, 'keydown', function (e) {
                if (e.ctrlKey && e.keyCode == key && !e.shiftKey && !e.altKey) {
                    func(e);
                    KE.event.stop(e);
                    return false;
                }
            }, id);
        },
        ready: function (func, win, doc, id) {
            var win = win || window;
            var doc = doc || document;
            var loaded = false;
            var readyFunc = function () {
                if (loaded) return;
                loaded = true;
                func();
            };
            if (doc.addEventListener) {
                this.add(doc, "DOMContentLoaded", readyFunc, id);
            } else if (doc.attachEvent) {
                this.add(doc, "readystatechange", function () {
                    if (doc.readyState == "complete") readyFunc();
                }, id);
                if (doc.documentElement.doScroll && typeof win.frameElement === "undefined") {
                    var ieReadyFunc = function () {
                        if (loaded) return;
                        try {
                            doc.documentElement.doScroll("left");
                        } catch (e) {
                            window.setTimeout(ieReadyFunc, 0);
                            return;
                        }
                        readyFunc();
                    };
                    ieReadyFunc();
                }
            }
            this.add(win, 'load', readyFunc, id);
        }
    };

    KE.each = function (obj, func) {
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) func(key, obj[key]);
        }
    };

    KE.eachNode = function (node, func) {
        var walkNodes = function (parent) {
            if (KE.util.getNodeType(parent) != 1) return true;
            var n = parent.firstChild;
            while (n) {
                var next = n.nextSibling;
                if (!func(n)) return false;
                if (!walkNodes(n)) return false;
                n = next;
            }
            return true;
        };
        walkNodes(node);
    };

    KE.selection = function (doc) {
        this.sel = null;
        this.range = null;
        this.keRange = null;
        this.isControl = false;
        var win = doc.parentWindow || doc.defaultView;
        this.init = function () {
            var sel = doc.selection ? doc.selection : win.getSelection();
            var range;
            try {
                if (sel.rangeCount > 0) range = sel.getRangeAt(0);
                else range = sel.createRange();
            } catch (e) { }
            if (!range) range = KE.util.createRange(doc);
            this.sel = sel;
            this.range = range;
            var startNode, startPos, endNode, endPos;
            if (KE.browser.IE) {
                if (range.item) {
                    this.isControl = true;
                    var el = range.item(0);
                    startNode = endNode = el;
                    startPos = endPos = 0;
                } else {
                    this.isControl = false;
                    var getStartEnd = function (isStart) {
                        var pointRange = range.duplicate();
                        pointRange.collapse(isStart);
                        var parentNode = pointRange.parentElement();
                        var nodes = parentNode.childNodes;
                        if (nodes.length == 0) return { node: parentNode, pos: 0 };
                        var startNode;
                        var endElement;
                        var startPos = 0;
                        var isEnd = false;
                        var testRange = range.duplicate();
                        KE.util.moveToElementText(testRange, parentNode);
                        for (var i = 0, len = nodes.length; i < len; i++) {
                            var node = nodes[i];
                            var cmp = testRange.compareEndPoints('StartToStart', pointRange);
                            if (cmp > 0) {
                                isEnd = true;
                            } else if (cmp == 0) {
                                if (node.nodeType == 1) {
                                    var keRange = new KE.range(doc);
                                    keRange.selectTextNode(node);
                                    return { node: keRange.startNode, pos: 0 };
                                } else {
                                    return { node: node, pos: 0 };
                                }
                            }
                            if (node.nodeType == 1) {
                                var nodeRange = range.duplicate();
                                KE.util.moveToElementText(nodeRange, node);
                                testRange.setEndPoint('StartToEnd', nodeRange);
                                if (isEnd) startPos += nodeRange.text.replace(/\r\n|\n|\r/g, '').length;
                                else startPos = 0;
                            } else if (node.nodeType == 3) {
                                //fix bug: typeof node.nodeValue can return "unknown" in IE.
                                if (typeof node.nodeValue === 'string') {
                                    testRange.moveStart('character', node.nodeValue.length);
                                    startPos += node.nodeValue.length;
                                }
                            }
                            if (!isEnd) startNode = node;
                        }
                        if (!isEnd && startNode.nodeType == 1) {
                            var startNode = parentNode.lastChild;
                            return { node: startNode, pos: startNode.nodeType == 1 ? 1 : startNode.nodeValue.length };
                        }
                        testRange = range.duplicate();
                        KE.util.moveToElementText(testRange, parentNode);
                        testRange.setEndPoint('StartToEnd', pointRange);
                        startPos -= testRange.text.replace(/\r\n|\n|\r/g, '').length;
                        return { node: startNode, pos: startPos };
                    };
                    var start = getStartEnd(true);
                    var end = getStartEnd(false);
                    startNode = start.node;
                    startPos = start.pos;
                    endNode = end.node;
                    endPos = end.pos;
                }
            } else {
                startNode = range.startContainer;
                startPos = range.startOffset;
                endNode = range.endContainer;
                endPos = range.endOffset;
                if (startNode.nodeType == 1 && typeof startNode.childNodes[startPos] != 'undefined') {
                    startNode = startNode.childNodes[startPos];
                    startPos = 0;
                }
                if (endNode.nodeType == 1) {
                    endPos = endPos == 0 ? 1 : endPos;
                    if (typeof endNode.childNodes[endPos - 1] != 'undefined') {
                        endNode = endNode.childNodes[endPos - 1];
                        endPos = (endNode.nodeType == 1) ? 0 : endNode.nodeValue.length;
                    }
                }
                this.isControl = (startNode.nodeType == 1 && startNode === endNode && range.startOffset + 1 == range.endOffset);
                if (startNode.nodeType == 1 && endNode.nodeType == 3 && endPos == 0 && endNode.previousSibling) {
                    var node = endNode.previousSibling;
                    while (node) {
                        if (node === startNode) {
                            endNode = startNode;
                            break;
                        }
                        if (node.childNodes.length != 1) break;
                        node = node.childNodes[0];
                    }
                }
                if (range.collapsed) {
                    var keRange = new KE.range(doc);
                    keRange.setTextStart(startNode, startPos);
                    endNode = keRange.startNode;
                    endPos = keRange.startPos;
                }
            }
            var keRange = new KE.range(doc);
            keRange.setTextStart(startNode, startPos);
            keRange.setTextEnd(endNode, endPos);
            this.keRange = keRange;
        };
        this.init();
        this.addRange = function (keRange) {
            if (KE.browser.GECKO && KE.browser.VERSION < 3) return;
            this.keRange = keRange;
            if (KE.browser.IE) {
                var getEndRange = function (isStart) {
                    var range = KE.util.createRange(doc);
                    var node = isStart ? keRange.startNode : keRange.endNode;
                    if (node.nodeType == 1) {
                        KE.util.moveToElementText(range, node);
                        range.collapse(isStart);
                    } else if (node.nodeType == 3) {
                        range = KE.util.getNodeStartRange(doc, node);
                        var pos = isStart ? keRange.startPos : keRange.endPos;
                        range.moveStart('character', pos);
                    }
                    return range;
                }
                if (!this.range.item) {
                    var node = keRange.startNode;
                    if (node == keRange.endNode && KE.util.getNodeType(node) == 1 && KE.util.getNodeTextLength(node) == 0) {
                        var temp = doc.createTextNode(" ");
                        node.appendChild(temp);
                        KE.util.moveToElementText(this.range, node);
                        this.range.collapse(false);
                        this.range.select();
                        node.removeChild(temp);
                    } else {
                        if (node.nodeType == 3 && keRange.collapsed()) {
                            this.range = getEndRange(true);
                            this.range.collapse(true);
                        } else {
                            this.range.setEndPoint('StartToStart', getEndRange(true));
                            this.range.setEndPoint('EndToStart', getEndRange(false));
                        }
                        this.range.select();
                    }
                }
            } else {
                var getNodePos = function (node) {
                    var pos = 0;
                    while (node) {
                        node = node.previousSibling;
                        pos++;
                    }
                    return --pos;
                };
                var range = new KE.range(doc);
                range.setTextStart(keRange.startNode, keRange.startPos);
                range.setTextEnd(keRange.endNode, keRange.endPos);
                var startNode = range.startNode;
                var endNode = range.endNode;
                if (KE.util.getNodeType(startNode) == 88) {
                    this.range.setStart(startNode.parentNode, getNodePos(range.startNode));
                } else {
                    this.range.setStart(startNode, range.startPos);
                }
                if (KE.util.getNodeType(endNode) == 88) {
                    this.range.setEnd(endNode.parentNode, getNodePos(range.endNode) + 1);
                } else {
                    this.range.setEnd(endNode, range.endPos);
                }
                this.sel.removeAllRanges();
                this.sel.addRange(this.range);
            }
        };
        this.focus = function () {
            if (KE.browser.IE && this.range != null) this.range.select();
        }
    };

    KE.range = function (doc) {
        this.startNode = null;
        this.startPos = null;
        this.endNode = null;
        this.endPos = null;
        this.getParentElement = function () {
            var scanParent = function (node, func) {
                while (node && (!node.tagName || node.tagName.toLowerCase() != 'body')) {
                    node = node.parentNode;
                    if (func(node)) return;
                }
            }
            var nodeList = [];
            scanParent(this.startNode, function (node) {
                nodeList.push(node);
            });
            var parentNode;
            scanParent(this.endNode, function (node) {
                if (KE.util.inArray(node, nodeList)) {
                    parentNode = node;
                    return true;
                }
            });
            return parentNode ? parentNode : doc.body;
        };
        this.getNodeList = function () {
            var self = this;
            var parentNode = this.getParentElement();
            var nodeList = [];
            var isStarted = false;
            if (parentNode == self.startNode) isStarted = true;
            if (isStarted) nodeList.push(parentNode);
            KE.eachNode(parentNode, function (node) {
                if (node == self.startNode) isStarted = true;
                var range = new KE.range(doc);
                range.selectTextNode(node);
                var cmp = range.comparePoints('START_TO_END', self);
                if (cmp > 0) {
                    return false;
                } else if (cmp == 0) {
                    if (range.startNode !== range.endNode || range.startPos !== range.endPos) return false;
                }
                if (isStarted) nodeList.push(node);
                return true;
            });
            return nodeList;
        };
        this.comparePoints = function (how, range) {
            var compareNodes = function (nodeA, posA, nodeB, posB) {
                var cmp;
                if (KE.browser.IE) {
                    var getStartRange = function (node, pos, isStart) {
                        var range = KE.util.createRange(doc);
                        var type = KE.util.getNodeType(node);
                        if (type == 1) {
                            KE.util.moveToElementText(range, node);
                            range.collapse(isStart);
                        } else if (type == 3) {
                            range = KE.util.getNodeStartRange(doc, node);
                            range.moveStart('character', pos);
                            range.collapse(true);
                        }
                        return range;
                    }
                    var rangeA, rangeB;
                    if (how == 'START_TO_START' || how == 'START_TO_END') rangeA = getStartRange(nodeA, posA, true);
                    else rangeA = getStartRange(nodeA, posA, false);
                    if (how == 'START_TO_START' || how == 'END_TO_START') rangeB = getStartRange(nodeB, posB, true);
                    else rangeB = getStartRange(nodeB, posB, false);
                    return rangeA.compareEndPoints('StartToStart', rangeB);
                } else {
                    var rangeA = KE.util.createRange(doc);
                    rangeA.selectNode(nodeA);
                    if (how == 'START_TO_START' || how == 'START_TO_END') rangeA.collapse(true);
                    else rangeA.collapse(false);
                    var rangeB = KE.util.createRange(doc);
                    rangeB.selectNode(nodeB);
                    if (how == 'START_TO_START' || how == 'END_TO_START') rangeB.collapse(true);
                    else rangeB.collapse(false);
                    if (rangeA.compareBoundaryPoints(Range.START_TO_START, rangeB) > 0) {
                        cmp = 1;
                    } else if (rangeA.compareBoundaryPoints(Range.START_TO_START, rangeB) == 0) {
                        if (posA > posB) cmp = 1;
                        else if (posA == posB) cmp = 0;
                        else cmp = -1;
                    } else {
                        cmp = -1;
                    }
                }
                return cmp;
            }
            if (how == 'START_TO_START') return compareNodes(this.startNode, this.startPos, range.startNode, range.startPos);
            if (how == 'START_TO_END') return compareNodes(this.startNode, this.startPos, range.endNode, range.endPos);
            if (how == 'END_TO_START') return compareNodes(this.endNode, this.endPos, range.startNode, range.startPos);
            if (how == 'END_TO_END') return compareNodes(this.endNode, this.endPos, range.endNode, range.endPos);
        };
        this.collapsed = function () {
            return (this.startNode === this.endNode && this.startPos === this.endPos);
        };
        this.collapse = function (toStart) {
            if (toStart) {
                this.setEnd(this.startNode, this.startPos);
            } else {
                this.setStart(this.endNode, this.endPos);
            }
        };
        this.setTextStart = function (node, pos) {
            var textNode = node;
            KE.eachNode(node, function (n) {
                if (KE.util.getNodeType(n) == 3 && n.nodeValue.length > 0 || KE.util.getNodeType(n) == 88) {
                    textNode = n;
                    pos = 0;
                    return false;
                }
                return true;
            });
            this.setStart(textNode, pos);
        };
        this.setStart = function (node, pos) {
            this.startNode = node;
            this.startPos = pos;
            if (this.endNode === null) {
                this.endNode = node;
                this.endPos = pos;
            }
        };
        this.setTextEnd = function (node, pos) {
            var textNode = node;
            KE.eachNode(node, function (n) {
                if (KE.util.getNodeType(n) == 3 && n.nodeValue.length > 0 || KE.util.getNodeType(n) == 88) {
                    textNode = n;
                    pos = KE.util.getNodeType(n) == 3 ? n.nodeValue.length : 0;
                }
                return true;
            });
            this.setEnd(textNode, pos);
        };
        this.setEnd = function (node, pos) {
            this.endNode = node;
            this.endPos = pos;
            if (this.startNode === null) {
                this.startNode = node;
                this.startPos = pos;
            }
        };
        this.selectNode = function (node) {
            this.setStart(node, 0);
            this.setEnd(node, node.nodeType == 1 ? 0 : node.nodeValue.length);
        };
        this.selectTextNode = function (node) {
            this.setTextStart(node, 0);
            this.setTextEnd(node, node.nodeType == 1 ? 0 : node.nodeValue.length);
        };
        this.extractContents = function (isDelete) {
            isDelete = (isDelete === undefined) ? true : isDelete;
            var thisRange = this;
            var startNode = this.startNode;
            var startPos = this.startPos;
            var endNode = this.endNode;
            var endPos = this.endPos;
            var removedNodes = [];
            function extractTextNode(node, startPos, endPos) {
                var length = node.nodeValue.length;
                var cloneNode = node.cloneNode(true);
                var centerNode = cloneNode.splitText(startPos);
                centerNode.splitText(endPos - startPos);
                if (isDelete) {
                    var center = node;
                    if (startPos > 0) center = node.splitText(startPos);
                    if (endPos < length) center.splitText(endPos - startPos);
                    removedNodes.push(center);
                }
                return centerNode;
            };
            var noEndTagHash = KE.util.arrayToHash(KE.setting.noEndTags);
            var isStarted = false;
            var isEnd = false;
            function extractNodes(parent, frag) {
                if (KE.util.getNodeType(parent) != 1) return true;
                var node = parent.firstChild;
                while (node) {
                    if (node == startNode) isStarted = true;
                    if (node == endNode) isEnd = true;
                    var nextNode = node.nextSibling;
                    var type = node.nodeType;
                    if (type == 1) {
                        var range = new KE.range(doc);
                        range.selectNode(node);
                        var cmp = range.comparePoints('END_TO_END', thisRange);
                        if (isStarted && (cmp < 0 || (cmp == 0 && noEndTagHash[node.nodeName.toLowerCase()] !== undefined))) {
                            var cloneNode = node.cloneNode(true);
                            frag.appendChild(cloneNode);
                            if (isDelete) {
                                node.parentNode.removeChild(node);
                            }
                        } else {
                            var childFlag = node.cloneNode(false);
                            if (noEndTagHash[childFlag.nodeName.toLowerCase()] === undefined) {
                                frag.appendChild(childFlag);
                                if (!extractNodes(node, childFlag)) return false;
                            }
                        }
                    } else if (type == 3) {
                        if (isStarted) {
                            var textNode;
                            if (node == startNode && node == endNode) {
                                textNode = extractTextNode(node, startPos, endPos);
                                frag.appendChild(textNode);
                                return false;
                            } else if (node == startNode) {
                                textNode = extractTextNode(node, startPos, node.nodeValue.length);
                                frag.appendChild(textNode);
                            } else if (node == endNode) {
                                textNode = extractTextNode(node, 0, endPos);
                                frag.appendChild(textNode);
                                return false;
                            } else {
                                textNode = extractTextNode(node, 0, node.nodeValue.length);
                                frag.appendChild(textNode);
                            }
                        }
                    }
                    node = nextNode;
                    if (isEnd) return false;
                }
                if (frag.innerHTML.replace(/<.*?>/g, '') === '' && frag.parentNode) {
                    frag.parentNode.removeChild(frag);
                }
                return true;
            }
            var parentNode = this.getParentElement();
            var docFrag = parentNode.cloneNode(false);
            extractNodes(parentNode, docFrag);
            KE.each(removedNodes, function (key, val) {
                if (val.nodeType != 3 || val.nodeValue.length > 0) {
                    val.parentNode.removeChild(val);
                }
            });
            return docFrag;
        };
        this.cloneContents = function () {
            return this.extractContents(false);
        };
        this.getText = function () {
            var html = this.cloneContents().innerHTML;
            return html.replace(/<.*?>/g, "");
        };
    };

    KE.cmd = function (id) {
        this.doc = KE.g[id].iframeDoc;
        this.keSel = KE.g[id].keSel;
        this.keRange = KE.g[id].keRange;
        this.mergeAttributes = function (el, attr) {
            for (var i = 0, len = attr.length; i < len; i++) {
                KE.each(attr[i], function (key, value) {
                    if (key.charAt(0) == '.') {
                        var jsKey = KE.util.getJsKey(key.substr(1));
                        el.style[jsKey] = value;
                    } else {
                        if (KE.browser.IE && KE.browser.VERSION < 8 && key == 'class') key = 'className';
                        el.setAttribute(key, value);
                    }
                });
            }
            return el;
        };
        this.wrapTextNode = function (node, startPos, endPos, element, attributes) {
            var length = node.nodeValue.length;
            var isFull = (startPos == 0 && endPos == length);
            var range = new KE.range(this.doc);
            range.selectTextNode(node.parentNode);
            if (isFull &&
			node.parentNode.tagName == element.tagName &&
			range.comparePoints('END_TO_END', this.keRange) <= 0 &&
			range.comparePoints('START_TO_START', this.keRange) >= 0) {
                this.mergeAttributes(node.parentNode, attributes);
                return node;
            } else {
                var el = element.cloneNode(true);
                if (isFull) {
                    var cloneNode = node.cloneNode(true);
                    el.appendChild(cloneNode);
                    node.parentNode.replaceChild(el, node);
                    return cloneNode;
                } else {
                    var centerNode = node;
                    if (startPos < endPos) {
                        if (startPos > 0) centerNode = node.splitText(startPos);
                        if (endPos < length) centerNode.splitText(endPos - startPos);
                        var cloneNode = centerNode.cloneNode(true);
                        el.appendChild(cloneNode);
                        centerNode.parentNode.replaceChild(el, centerNode);
                        return cloneNode;
                    } else {
                        if (startPos < length) {
                            centerNode = node.splitText(startPos);
                            centerNode.parentNode.insertBefore(el, centerNode);
                        } else {
                            if (centerNode.nextSibling) {
                                centerNode.parentNode.insertBefore(el, centerNode.nextSibling);
                            } else {
                                centerNode.parentNode.appendChild(el);
                            }
                        }
                        return el;
                    }
                }
            }
        };
        this.wrap = function (tagName, attributes) {
            attributes = attributes || [];
            var self = this;
            this.keSel.focus();
            var element = KE.$$(tagName, this.doc);
            this.mergeAttributes(element, attributes);
            var keRange = this.keRange;
            var startNode = keRange.startNode;
            var startPos = keRange.startPos;
            var endNode = keRange.endNode;
            var endPos = keRange.endPos;
            var parentNode = keRange.getParentElement();
            if (KE.util.inMarquee(parentNode)) return;
            var isStarted = false;
            KE.eachNode(parentNode, function (node) {
                if (node == startNode) isStarted = true;
                if (node.nodeType == 1) {
                    if (node == startNode && node == endNode) {
                        if (KE.util.inArray(node.tagName.toLowerCase(), KE.g[id].noEndTags)) {
                            if (startPos > 0) node.parentNode.appendChild(element);
                            else node.parentNode.insertBefore(element, node);
                        } else {
                            node.appendChild(element);
                        }
                        keRange.selectNode(element);
                        return false;
                    } else if (node == startNode) {
                        keRange.setStart(node, 0);
                    } else if (node == endNode) {
                        keRange.setEnd(node, 0);
                        return false;
                    }
                } else if (node.nodeType == 3) {
                    if (isStarted) {
                        if (node == startNode && node == endNode) {
                            var rangeNode = self.wrapTextNode(node, startPos, endPos, element, attributes);
                            keRange.selectNode(rangeNode);
                            return false;
                        } else if (node == startNode) {
                            var rangeNode = self.wrapTextNode(node, startPos, node.nodeValue.length, element, attributes);
                            keRange.setStart(rangeNode, 0);
                        } else if (node == endNode) {
                            var rangeNode = self.wrapTextNode(node, 0, endPos, element, attributes);
                            keRange.setEnd(rangeNode, rangeNode.nodeType == 1 ? 0 : rangeNode.nodeValue.length);
                            return false;
                        } else {
                            self.wrapTextNode(node, 0, node.nodeValue.length, element, attributes);
                        }
                    }
                }
                return true;
            });
            this.keSel.addRange(keRange);
        };
        this.getTopParent = function (tagNames, node) {
            var parent = null;
            while (node) {
                node = node.parentNode;
                if (KE.util.inArray(node.tagName.toLowerCase(), tagNames)) {
                    parent = node;
                } else {
                    break;
                }
            }
            return parent;
        };
        this.splitNodeParent = function (parent, node, pos) {
            var leftRange = new KE.range(this.doc);
            leftRange.selectNode(parent.firstChild);
            leftRange.setEnd(node, pos);
            var leftFrag = leftRange.extractContents();
            parent.parentNode.insertBefore(leftFrag, parent);
            return { left: leftFrag, right: parent };
        };
        this.remove = function (tags) {
            var self = this;
            var keRange = this.keRange;
            var startNode = keRange.startNode;
            var startPos = keRange.startPos;
            var endNode = keRange.endNode;
            var endPos = keRange.endPos;
            this.keSel.focus();
            if (KE.util.inMarquee(keRange.getParentElement())) return;
            var isCollapsed = (keRange.getText().replace(/\s+/g, '') === '');
            if (isCollapsed && !KE.browser.IE) return;
            var tagNames = [];
            KE.each(tags, function (key, val) {
                if (key != '*') tagNames.push(key);
            });
            var startParent = this.getTopParent(tagNames, startNode);
            var endParent = this.getTopParent(tagNames, endNode);
            if (startParent) {
                var startFrags = this.splitNodeParent(startParent, startNode, startPos);
                keRange.setStart(startFrags.right, 0);
                if (startNode == endNode && KE.util.getNodeTextLength(startFrags.right) > 0) {
                    keRange.selectNode(startFrags.right);
                    var range = new KE.range(this.doc);
                    range.selectTextNode(startFrags.left);
                    if (startPos > 0) endPos -= range.endNode.nodeValue.length;
                    range.selectTextNode(startFrags.right);
                    endNode = range.startNode;
                }
            }
            if (isCollapsed) {
                var node = keRange.startNode;
                if (node.nodeType == 1) {
                    if (node.nodeName.toLowerCase() == 'br') return;
                    keRange.selectNode(node);
                } else {
                    return;
                }
            } else if (endParent) {
                var endFrags = this.splitNodeParent(endParent, endNode, endPos);
                keRange.setEnd(endFrags.left, 0);
                if (startParent == endParent) {
                    keRange.setStart(endFrags.left, 0);
                }
            }
            var removeAttr = function (node, attr) {
                if (attr.charAt(0) == '.') {
                    var jsKey = KE.util.getJsKey(attr.substr(1));
                    node.style[jsKey] = '';
                } else {
                    if (KE.browser.IE && KE.browser.VERSION < 8 && attr == 'class') attr = 'className';
                    node.removeAttribute(attr);
                }
            };
            var nodeList = keRange.getNodeList();
            keRange.setTextStart(keRange.startNode, keRange.startPos);
            keRange.setTextEnd(keRange.endNode, keRange.endPos);
            for (var i = 0, length = nodeList.length; i < length; i++) {
                var node = nodeList[i];
                if (node.nodeType == 1) {
                    var tagName = node.tagName.toLowerCase();
                    if (tags[tagName]) {
                        var attr = tags[tagName];
                        for (var j = 0, len = attr.length; j < len; j++) {
                            if (attr[j] == '*') {
                                KE.util.removeParent(node);
                                break;
                            } else {
                                removeAttr(node, attr[j]);
                                var attrs = [];
                                if (node.outerHTML) {
                                    attrHash = KE.util.getAttrList(node.outerHTML);
                                    KE.each(attrHash, function (key, val) {
                                        attrs.push({
                                            name: key,
                                            value: val
                                        });
                                    });
                                } else {
                                    attrs = node.attributes;
                                }
                                if (attrs.length == 0) {
                                    KE.util.removeParent(node);
                                    break;
                                } else if (attrs[0].name == 'style' && attrs[0].value === '') {
                                    KE.util.removeParent(node);
                                    break;
                                }
                            }
                        }
                    }
                    if (tags['*']) {
                        var attr = tags['*'];
                        for (var j = 0, len = attr.length; j < len; j++) {
                            removeAttr(node, attr[j]);
                        }
                    }
                }
            }
            try {
                this.keSel.addRange(keRange);
            } catch (e) { }
        };
    };

    KE.format = {
        getUrl: function (url, mode, host, pathname) {
            if (!mode) return url;
            mode = mode.toLowerCase();
            if (!KE.util.inArray(mode, ['absolute', 'relative', 'domain'])) return url;
            host = host || location.protocol + '//' + location.host;
            if (pathname === undefined) {
                var m = location.pathname.match(/^(\/.*)\//);
                pathname = m ? m[1] : '';
            }
            var matches = url.match(/^(\w+:\/\/[^\/]*)/);
            if (matches) {
                if (matches[1] !== host) return url;
            } else if (url.match(/^\w+:/)) {
                return url;
            }
            var getRealPath = function (path) {
                var parts = path.split('/');
                paths = [];
                for (var i = 0, len = parts.length; i < len; i++) {
                    var part = parts[i];
                    if (part == '..') {
                        if (paths.length > 0) paths.pop();
                    } else if (part !== '' && part != '.') {
                        paths.push(part);
                    }
                }
                return '/' + paths.join('/');
            };
            if (url.match(/^\//)) {
                url = host + getRealPath(url.substr(1));
            } else if (!url.match(/^\w+:\/\//)) {
                url = host + getRealPath(pathname + '/' + url);
            }
            if (mode == 'relative') {
                var getRelativePath = function (path, depth) {
                    if (url.substr(0, path.length) === path) {
                        var arr = [];
                        for (var i = 0; i < depth; i++) {
                            arr.push('..');
                        }
                        var prefix = '.';
                        if (arr.length > 0) prefix += '/' + arr.join('/');
                        if (pathname == '/') prefix += '/';
                        return prefix + url.substr(path.length);
                    } else {
                        var m = path.match(/^(.*)\//);
                        if (m) {
                            return getRelativePath(m[1], ++depth);
                        }
                    }
                };
                url = getRelativePath(host + pathname, 0).substr(2);
            } else if (mode == 'absolute') {
                if (url.substr(0, host.length) === host) {
                    url = url.substr(host.length);
                }
            }
            return url;
        },
        getHtml: function (html, htmlTags, urlType) {
            var isFilter = htmlTags ? true : false;
            html = html.replace(/(<pre[^>]*>)([\s\S]*?)(<\/pre>)/ig, function ($0, $1, $2, $3) {
                return $1 + $2.replace(/<br[^>]*>/ig, '\n') + $3;
            });
            var htmlTagHash = {};
            var fontSizeHash = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
            if (isFilter) {
                KE.each(htmlTags, function (key, val) {
                    var arr = key.split(',');
                    for (var i = 0, len = arr.length; i < len; i++) htmlTagHash[arr[i]] = KE.util.arrayToHash(val);
                });
            }
            var skipFlag = false;
            var noEndTagHash = KE.util.arrayToHash(KE.setting.noEndTags);
            var inlineTagHash = KE.util.arrayToHash(KE.setting.inlineTags);
            var endlineTagHash = KE.util.arrayToHash(KE.setting.endlineTags);
            var re = /((?:\r\n|\n|\r)*)<(\/)?([\w\-:]+)((?:\s+|(?:\s+[\w\-:]+)|(?:\s+[\w\-:]+=[^\s"'<>]+)|(?:\s+[\w\-:]+="[^"]*")|(?:\s+[\w\-:]+='[^']*'))*)(\/)?>((?:\r\n|\n|\r)*)/g;
            html = html.replace(re, function ($0, $1, $2, $3, $4, $5, $6) {
                var startNewline = $1 || '';
                var startSlash = $2 || '';
                var tagName = $3.toLowerCase();
                var attr = $4 || '';
                var endSlash = $5 ? ' ' + $5 : '';
                var endNewline = $6 || '';
                if (tagName === 'script' && startSlash !== '') skipFlag = false;
                if (skipFlag) return $0;
                if (tagName === 'script' && startSlash === '') skipFlag = true;
                if (isFilter && typeof htmlTagHash[tagName] == "undefined") return '';
                if (endSlash === '' && typeof noEndTagHash[tagName] != "undefined") endSlash = ' /';
                if (tagName in endlineTagHash) {
                    if (startSlash || endSlash) endNewline = '\n';
                } else {
                    if (endNewline) endNewline = ' ';
                }
                if (tagName !== 'script' && tagName !== 'style') {
                    startNewline = '';
                }
                if (tagName === 'font') {
                    var style = {}, styleStr = '';
                    attr = attr.replace(/\s*([\w\-:]+)=([^\s"'<>]+|"[^"]*"|'[^']*')/g, function ($0, $1, $2) {
                        var key = $1.toLowerCase();
                        var val = $2 || '';
                        val = val.replace(/^["']|["']$/g, '');
                        if (key === 'color') {
                            style['color'] = val;
                            return ' ';
                        }
                        if (key === 'size') {
                            style['font-size'] = fontSizeHash[parseInt(val) - 1] || '';
                            return ' ';
                        }
                        if (key === 'face') {
                            style['font-family'] = val;
                            return ' ';
                        }
                        if (key === 'style') {
                            styleStr = val;
                            return ' ';
                        }
                        return $0;
                    });
                    if (styleStr && !/;$/.test(styleStr)) styleStr += ';';
                    KE.each(style, function (key, val) {
                        if (val !== '') {
                            if (/\s/.test(val)) val = "'" + val + "'";
                            styleStr += key + ':' + val + ';';
                        }
                    });
                    if (styleStr) attr += ' style="' + styleStr + '"';
                    tagName = 'span';
                }
                if (attr !== '') {
                    attr = attr.replace(/\s*([\w\-:]+)=([^\s"'<>]+|"[^"]*"|'[^']*')/g, function ($0, $1, $2) {
                        var key = $1.toLowerCase();
                        var val = $2 || '';
                        if (isFilter) {
                            if (key.charAt(0) === "." || (key !== "style" && typeof htmlTagHash[tagName][key] == "undefined")) return ' ';
                        }
                        if (val === '') {
                            val = '""';
                        } else {
                            if (key === "style") {
                                val = val.substr(1, val.length - 2);
                                val = val.replace(/\s*([^\s]+?)\s*:(.*?)(;|$)/g, function ($0, $1, $2) {
                                    var k = $1.toLowerCase();
                                    if (isFilter) {
                                        if (typeof htmlTagHash[tagName]['style'] == "undefined" && typeof htmlTagHash[tagName]['.' + k] == "undefined") return '';
                                    }
                                    var v = KE.util.trim($2);
                                    v = KE.util.rgbToHex(v);
                                    return k + ':' + v + ';';
                                });
                                val = KE.util.trim(val);
                                if (val === '') return '';
                                val = '"' + val + '"';
                            }
                            if (KE.util.inArray(key, ['src', 'href'])) {
                                if (val.charAt(0) === '"') {
                                    val = val.substr(1, val.length - 2);
                                }
                                val = KE.format.getUrl(val, urlType);
                            }
                            if (val.charAt(0) !== '"') val = '"' + val + '"';
                        }
                        return ' ' + key + '=' + val + ' ';
                    });
                    attr = attr.replace(/\s+(checked|selected|disabled|readonly)(\s+|$)/ig, function ($0, $1) {
                        var key = $1.toLowerCase();
                        if (isFilter) {
                            if (key.charAt(0) === "." || typeof htmlTagHash[tagName][key] == "undefined") return ' ';
                        }
                        return ' ' + key + '="' + key + '"' + ' ';
                    });
                    attr = KE.util.trim(attr);
                    attr = attr.replace(/\s+/g, ' ');
                    if (attr) attr = ' ' + attr;
                    return startNewline + '<' + startSlash + tagName + attr + endSlash + '>' + endNewline;
                } else {
                    return startNewline + '<' + startSlash + tagName + endSlash + '>' + endNewline;
                }
            });
            if (!KE.browser.IE) {
                html = html.replace(/<p><br\s+\/>\n<\/p>/ig, '<p>&nbsp;</p>');
                html = html.replace(/<br\s+\/>\n<\/p>/ig, '</p>');
            }
            html = html.replace(/\u200B/g, '');
            var reg = KE.setting.inlineTags.join('|');
            var trimHtml = function (inHtml) {
                var outHtml = inHtml.replace(new RegExp('<(' + reg + ')[^>]*><\\/(' + reg + ')>', 'ig'), function ($0, $1, $2) {
                    if ($1 == $2) return '';
                    else return $0;
                });
                if (inHtml !== outHtml) outHtml = trimHtml(outHtml);
                return outHtml;
            };
            return KE.util.trim(trimHtml(html));
        }
    };

    KE.attr = function (el, key, val) {
        if (KE.browser.IE && KE.browser.VERSION < 8 && key.toLowerCase() == 'class') key = 'className';
        val = '' + val;
        el.setAttribute(key, val);
        if (val === '') el.removeAttribute(key);
    };

    KE.addClass = function (el, className) {
        if (typeof el == 'object') {
            var cls = el.className;
            if (cls) {
                if ((' ' + cls + ' ').indexOf(' ' + className + ' ') < 0) {
                    el.className = cls + ' ' + className;
                }
            } else {
                el.className = className;
            }
        } else if (typeof el == 'string') {
            if (/\s+class\s*=/.test(el)) {
                el = el.replace(/(\s+class=["']?)([^"']*)(["']?[\s>])/, function ($0, $1, $2, $3) {
                    if ((' ' + $2 + ' ').indexOf(' ' + className + ' ') < 0) {
                        return $2 === '' ? $1 + className + $3 : $1 + $2 + ' ' + className + $3;
                    } else {
                        return $0;
                    }
                });
            } else {
                el = el.substr(0, el.length - 1) + ' class="' + className + '">';
            }
        }
        return el;
    };

    KE.removeClass = function (el, className) {
        var cls = el.className || '';
        cls = ' ' + cls + ' ';
        className = ' ' + className + ' ';
        if (cls.indexOf(className) >= 0) {
            cls = KE.util.trim(cls.replace(new RegExp(className, 'ig'), ''));
            if (cls === '') {
                var key = el.getAttribute('class') ? 'class' : 'className';
                el.removeAttribute(key);
            } else {
                el.className = cls;
            }
        }
        return el;
    };

    KE.getComputedStyle = function (el, key) {
        var doc = el.ownerDocument,
		win = doc.parentWindow || doc.defaultView,
		jsKey = KE.util.getJsKey(key),
		val = '';
        if (win.getComputedStyle) {
            var style = win.getComputedStyle(el, null);
            val = style[jsKey] || style.getPropertyValue(key) || el.style[jsKey];
        } else if (el.currentStyle) {
            val = el.currentStyle[jsKey] || el.style[jsKey];
        }
        return val;
    };

    KE.getCommonAncestor = function (keSel, tagName) {
        var range = keSel.range,
		keRange = keSel.keRange,
		startNode = keRange.startNode,
		endNode = keRange.endNode;
        if (KE.util.inArray(tagName, ['table', 'td', 'tr'])) {
            if (KE.browser.IE) {
                if (range.item) {
                    if (range.item(0).nodeName.toLowerCase() === tagName) {
                        startNode = endNode = range.item(0);
                    }
                } else {
                    var rangeA = range.duplicate();
                    rangeA.collapse(true);
                    var rangeB = range.duplicate();
                    rangeB.collapse(false);
                    startNode = rangeA.parentElement();
                    endNode = rangeB.parentElement();
                }
            } else {
                var rangeA = range.cloneRange();
                rangeA.collapse(true);
                var rangeB = range.cloneRange();
                rangeB.collapse(false);
                startNode = rangeA.startContainer;
                endNode = rangeB.startContainer;
            }
        }
        function find(node) {
            while (node) {
                if (node.nodeType == 1) {
                    if (node.tagName.toLowerCase() === tagName) return node;
                }
                node = node.parentNode;
            }
            return null;
        };
        var start = find(startNode),
		end = find(endNode);
        if (start && end && start === end) {
            return start;
        }
        return null;
    };

    KE.queryCommandValue = function (doc, cmd) {
        cmd = cmd.toLowerCase();
        function commandValue() {
            var val = doc.queryCommandValue(cmd);
            if (typeof val !== 'string') val = '';
            return val;
        }
        var val = '';
        if (cmd === 'fontname') {
            val = commandValue();
            val = val.replace(/['"]/g, '');
        } else if (cmd === 'formatblock') {
            val = commandValue();
            if (val === '') {
                var keSel = new KE.selection(doc);
                var el = KE.getCommonAncestor(keSel, 'h1');
                if (!el) el = KE.getCommonAncestor(keSel, 'h2');
                if (!el) el = KE.getCommonAncestor(keSel, 'h3');
                if (!el) el = KE.getCommonAncestor(keSel, 'h4');
                if (!el) el = KE.getCommonAncestor(keSel, 'p');
                if (el) val = el.nodeName;
            }
            if (val === 'Normal') val = 'p';
        } else if (cmd === 'fontsize') {
            var keSel = new KE.selection(doc);
            var el = KE.getCommonAncestor(keSel, 'span');
            if (el) val = KE.getComputedStyle(el, 'font-size');
        } else if (cmd === 'textcolor') {
            var keSel = new KE.selection(doc);
            var el = KE.getCommonAncestor(keSel, 'span');
            if (el) val = KE.getComputedStyle(el, 'color');
            val = KE.util.rgbToHex(val);
            if (val === '') val = 'default';
        } else if (cmd === 'bgcolor') {
            var keSel = new KE.selection(doc);
            var el = KE.getCommonAncestor(keSel, 'span');
            if (el) val = KE.getComputedStyle(el, 'background-color');
            val = KE.util.rgbToHex(val);
            if (val === '') val = 'default';
        }
        return val.toLowerCase();
    };

    KE.util = {
        getDocumentElement: function (doc) {
            doc = doc || document;
            return (doc.compatMode != "CSS1Compat") ? doc.body : doc.documentElement;
        },
        getDocumentHeight: function (doc) {
            var el = this.getDocumentElement(doc);
            return Math.max(el.scrollHeight, el.clientHeight);
        },
        getDocumentWidth: function (doc) {
            var el = this.getDocumentElement(doc);
            return Math.max(el.scrollWidth, el.clientWidth);
        },
        createTable: function (doc) {
            var table = KE.$$('table', doc);
            table.cellPadding = 0;
            table.cellSpacing = 0;
            table.border = 0;
            return { table: table, cell: table.insertRow(0).insertCell(0) };
        },
        loadStyle: function (path) {
            var link = KE.$$('link');
            link.setAttribute('type', 'text/css');
            link.setAttribute('rel', 'stylesheet');
            link.setAttribute('href', path);
            document.getElementsByTagName("head")[0].appendChild(link);
        },
        getAttrList: function (tag) {
            var re = /\s+(?:([\w\-:]+)|(?:([\w\-:]+)=([\w\-:]+))|(?:([\w\-:]+)="([^"]*)")|(?:([\w\-:]+)='([^']*)'))(?=(?:\s|\/|>)+)/g;
            var arr, key, val, list = {};
            while ((arr = re.exec(tag))) {
                key = arr[1] || arr[2] || arr[4] || arr[6];
                val = arr[1] || (arr[2] ? arr[3] : (arr[4] ? arr[5] : arr[7]));
                list[key] = val;
            }
            return list;
        },
        inArray: function (str, arr) {
            for (var i = 0; i < arr.length; i++) { if (str == arr[i]) return true; }
            return false;
        },
        trim: function (str) {
            return str.replace(/^\s+|\s+$/g, "");
        },
        getJsKey: function (key) {
            var arr = key.split('-');
            key = '';
            for (var i = 0, len = arr.length; i < len; i++) {
                key += (i > 0) ? arr[i].charAt(0).toUpperCase() + arr[i].substr(1) : arr[i];
            }
            return key;
        },
        arrayToHash: function (arr) {
            var hash = {};
            for (var i = 0, len = arr.length; i < len; i++) hash[arr[i]] = 1;
            return hash;
        },
        escape: function (str) {
            str = str.replace(/&/g, '&amp;');
            str = str.replace(/</g, '&lt;');
            str = str.replace(/>/g, '&gt;');
            str = str.replace(/"/g, '&quot;');
            return str;
        },
        unescape: function (str) {
            str = str.replace(/&lt;/g, '<');
            str = str.replace(/&gt;/g, '>');
            str = str.replace(/&quot;/g, '"');
            str = str.replace(/&amp;/g, '&');
            return str;
        },
        getScrollPos: function () {
            var x, y;
            if (KE.browser.IE || KE.browser.OPERA) {
                var el = this.getDocumentElement();
                x = el.scrollLeft;
                y = el.scrollTop;
            } else {
                x = window.scrollX;
                y = window.scrollY;
            }
            return { x: x, y: y };
        },
        getElementPos: function (el) {
            var x = 0, y = 0;
            if (el.getBoundingClientRect) {
                var box = el.getBoundingClientRect();
                var pos = this.getScrollPos();
                x = box.left + pos.x;
                y = box.top + pos.y;
            } else {
                x = el.offsetLeft;
                y = el.offsetTop;
                var parent = el.offsetParent;
                while (parent) {
                    x += parent.offsetLeft;
                    y += parent.offsetTop;
                    parent = parent.offsetParent;
                }
            }
            return { x: x, y: y };
        },
        getCoords: function (ev) {
            ev = ev || window.event;
            return {
                x: ev.clientX,
                y: ev.clientY
            };
        },
        setOpacity: function (el, opacity) {
            if (typeof el.style.opacity == "undefined") {
                el.style.filter = (opacity == 100) ? "" : "alpha(opacity=" + opacity + ")";
            } else {
                el.style.opacity = (opacity == 100) ? "" : (opacity / 100);
            }
        },
        getIframeDoc: function (iframe) {
            return iframe.contentDocument || iframe.contentWindow.document;
        },
        rgbToHex: function (str) {
            function hex(s) {
                s = parseInt(s).toString(16);
                return s.length > 1 ? s : '0' + s;
            };
            return str.replace(/rgb\s*?\(\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?\)/ig,
			function ($0, $1, $2, $3) {
			    return '#' + hex($1) + hex($2) + hex($3);
			}
		);
        },
        parseJson: function (text) {
            //extract JSON string
            var match;
            if ((match = /\{[\s\S]*\}|\[[\s\S]*\]/.exec(text))) {
                text = match[0];
            }
            var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/.
		test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
		replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
		replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                return eval('(' + text + ')');
            }
            throw 'JSON parse error';
        },
        getParam: function (url, name) {
            return url.match(new RegExp('[?&]' + name + '=([^?&]+)', 'i')) ? unescape(RegExp.$1) : '';
        },
        createRange: function (doc) {
            return doc.body.createTextRange ? doc.body.createTextRange() : doc.createRange();
        },
        getNodeType: function (node) {
            return (node.nodeType == 1 && KE.util.inArray(node.tagName.toLowerCase(), KE.setting.noEndTags)) ? 88 : node.nodeType;
        },
        inMarquee: function (node) {
            var n = node, nodeName;
            while (n) {
                nodeName = n.nodeName.toLowerCase();
                if (nodeName == 'marquee' || nodeName == 'select') return true;
                n = n.parentNode;
            }
            return false;
        },
        moveToElementText: function (range, el) {
            if (!this.inMarquee(el)) range.moveToElementText(el);
        },
        getNodeTextLength: function (node) {
            var type = KE.util.getNodeType(node);
            if (type == 1) {
                var html = node.innerHTML;
                return html.replace(/<.*?>/ig, "").length;
            } else if (type == 3) {
                return node.nodeValue.length;
            }
        },
        getNodeStartRange: function (doc, node) {
            var range = KE.util.createRange(doc);
            var type = node.nodeType;
            if (type == 1) {
                KE.util.moveToElementText(range, node);
                return range;
            } else if (type == 3) {
                var offset = 0;
                var sibling = node.previousSibling;
                while (sibling) {
                    if (sibling.nodeType == 1) {
                        var nodeRange = KE.util.createRange(doc);
                        KE.util.moveToElementText(nodeRange, sibling);
                        range.setEndPoint('StartToEnd', nodeRange);
                        range.moveStart('character', offset);
                        return range;
                    } else if (sibling.nodeType == 3) {
                        offset += sibling.nodeValue.length;
                    }
                    sibling = sibling.previousSibling;
                }
                KE.util.moveToElementText(range, node.parentNode);
                range.moveStart('character', offset);
                return range;
            }
        },
        removeParent: function (parent) {
            if (parent.hasChildNodes) {
                var node = parent.firstChild;
                while (node) {
                    var nextNode = node.nextSibling;
                    parent.parentNode.insertBefore(node, parent);
                    node = nextNode;
                }
            }
            parent.parentNode.removeChild(parent);
        },
        pluginLang: function (pluginName, doc) {
            KE.each(KE.lang.plugins[pluginName], function (key, val) {
                var span = KE.$('lang.' + key, doc);
                if (span) {
                    span.parentNode.insertBefore(doc.createTextNode(val), span);
                    span.parentNode.removeChild(span);
                }
            });
        },
        drag: function (id, mousedownObj, moveObj, func) {
            var g = KE.g[id];
            mousedownObj.onmousedown = function (e) {
                var self = this;
                e = e || window.event;
                var pos = KE.util.getCoords(e);
                var objTop = parseInt(moveObj.style.top);
                var objLeft = parseInt(moveObj.style.left);
                var objWidth = moveObj.style.width;
                var objHeight = moveObj.style.height;
                if (objWidth.match(/%$/)) objWidth = moveObj.offsetWidth + 'px';
                if (objHeight.match(/%$/)) objHeight = moveObj.offsetHeight + 'px';
                objWidth = parseInt(objWidth);
                objHeight = parseInt(objHeight);
                var mouseTop = pos.y;
                var mouseLeft = pos.x;
                var scrollPos = KE.util.getScrollPos();
                var scrollTop = scrollPos.y;
                var scrollLeft = scrollPos.x;
                var dragFlag = true;
                function moveListener(e) {
                    if (dragFlag) {
                        var pos = KE.util.getCoords(e);
                        var scrollPos = KE.util.getScrollPos();
                        var top = parseInt(pos.y - mouseTop - scrollTop + scrollPos.y);
                        var left = parseInt(pos.x - mouseLeft - scrollLeft + scrollPos.x);
                        func(objTop, objLeft, objWidth, objHeight, top, left);
                    }
                }
                var iframePos = KE.util.getElementPos(g.iframe);
                function iframeMoveListener(e) {
                    if (dragFlag) {
                        var pos = KE.util.getCoords(e, g.iframeDoc);
                        var top = parseInt(iframePos.y + pos.y - mouseTop - scrollTop);
                        var left = parseInt(iframePos.x + pos.x - mouseLeft - scrollLeft);
                        func(objTop, objLeft, objWidth, objHeight, top, left);
                    }
                }
                var selectListener = function () { return false; };
                function upListener(e) {
                    dragFlag = false;
                    if (self.releaseCapture) self.releaseCapture();
                    KE.event.remove(document, 'mousemove', moveListener);
                    KE.event.remove(document, 'mouseup', upListener);
                    KE.event.remove(g.iframeDoc, 'mousemove', iframeMoveListener);
                    KE.event.remove(g.iframeDoc, 'mouseup', upListener);
                    KE.event.remove(document, 'selectstart', selectListener);
                    KE.event.stop(e);
                    return false;
                }
                KE.event.add(document, 'mousemove', moveListener);
                KE.event.add(document, 'mouseup', upListener);
                KE.event.add(g.iframeDoc, 'mousemove', iframeMoveListener);
                KE.event.add(g.iframeDoc, 'mouseup', upListener);
                KE.event.add(document, 'selectstart', selectListener);
                if (self.setCapture) self.setCapture();
                KE.event.stop(e);
                return false;
            };
        },
        resize: function (id, width, height, isCheck, isResizeWidth) {
            isResizeWidth = (typeof isResizeWidth == "undefined") ? true : isResizeWidth;
            var g = KE.g[id];
            if (!g.container) return;
            if (isCheck && (parseInt(width) <= g.minWidth || parseInt(height) <= g.minHeight)) return;
            if (isResizeWidth) g.container.style.width = width;
            if (KE.browser.IE) {
                //improve IE performance (issue #126)
                var temp = g.toolbarTable && g.toolbarTable.offsetHeight;
            }
            g.container.style.height = height;
            var diff = parseInt(height) - g.toolbarHeight - g.statusbarHeight;
            if (diff >= 0) {
                g.iframe.style.height = diff + 'px';
                g.newTextarea.style.height = (((KE.browser.IE && KE.browser.VERSION < 8 || document.compatMode != 'CSS1Compat') && diff >= 2) ? diff - 2 : diff) + 'px';
            }
        },
        hideLoadingPage: function (id) {
            var stack = KE.g[id].dialogStack;
            var dialog = stack[stack.length - 1];
            dialog.loading.style.display = 'none';
            dialog.iframe.style.display = '';
        },
        showLoadingPage: function (id) {
            var stack = KE.g[id].dialogStack;
            var dialog = stack[stack.length - 1];
            dialog.loading.style.display = '';
            dialog.iframe.style.display = 'none';
        },
        setDefaultPlugin: function (id) {
            var items = [
			'selectall', 'justifyleft', 'justifycenter', 'justifyright', 'justifyfull',
			'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
			'superscript', 'bold', 'italic', 'underline', 'strikethrough'
		];
            var shortcuts = {
                bold: 'B',
                italic: 'I',
                underline: 'U'
            };
            for (var i = 0; i < items.length; i++) {
                var item = items[i],
				plugin = {};
                if (item in shortcuts) {
                    plugin.init = (function (item) {
                        return function (id) {
                            KE.event.ctrl(KE.g[id].iframeDoc, shortcuts[item], function (e) {
                                KE.plugin[item].click(id);
                                KE.util.focus(id);
                            }, id);
                        };
                    })(item);
                }
                plugin.click = (function (item) {
                    return function (id) {
                        KE.util.execCommand(id, item, null);
                    };
                })(item);
                KE.plugin[item] = plugin;
            }
        },
        getFullHtml: function (id) {
            var html = '<html>';
            html += '<head>';
            html += '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
            html += '<title>KindEditor</title>';
            html += '<link href="' + KE.g[id].skinsPath + 'common/editor.css?ver=' + escape(KE.version) + '" rel="stylesheet" type="text/css" />';
            var cssPath = KE.g[id].cssPath;
            if (typeof cssPath == 'string') cssPath = [cssPath];
            for (var i = 0, len = cssPath.length; i < len; i++) {
                if (cssPath[i] !== '') html += '<link href="' + cssPath[i] + '" rel="stylesheet" type="text/css" />';
            }
            html += '</head>';
            html += '<body class="ke-content"></body>';
            html += '</html>';
            return html;
        },
        getMediaType: function (src) {
            if (src.match(/\.(rm|rmvb)(\?|$)/i)) return 'rm';
            else if (src.match(/\.(swf|flv)(\?|$)/i)) return 'flash';
            else return 'media';
        },
        getMediaImage: function (id, type, attrs) {
            var width = attrs.width;
            var height = attrs.height;
            type = type || this.getMediaType(attrs.src);
            var srcTag = this.getMediaEmbed(attrs);
            var style = '';
            if (width > 0) style += 'width:' + width + 'px;';
            if (height > 0) style += 'height:' + height + 'px;';
            var className = 'ke-' + type;
            var html = '<img class="' + className + '" src="' + KE.g[id].skinsPath + 'common/blank.gif" ';
            if (style !== '') html += 'style="' + style + '" ';
            html += 'kesrctag="' + escape(srcTag) + '" alt="" />';
            return html;
        },
        getMediaEmbed: function (attrs) {
            var html = '<embed ';
            KE.each(attrs, function (key, val) {
                html += key + '="' + val + '" ';
            });
            html += '/>';
            return html;
        },
        execGetHtmlHooks: function (id, html) {
            var hooks = KE.g[id].getHtmlHooks;
            for (var i = 0, len = hooks.length; i < len; i++) {
                html = hooks[i](html);
            }
            return html;
        },
        execSetHtmlHooks: function (id, html) {
            var hooks = KE.g[id].setHtmlHooks;
            for (var i = 0, len = hooks.length; i < len; i++) {
                html = hooks[i](html);
            }
            return html;
        },
        execOnchangeHandler: function (id) {
            var handlers = KE.g[id].onchangeHandlerStack;
            for (var i = 0, len = handlers.length; i < len; i++) {
                handlers[i]();
            }
        },
        toData: function (id, srcData) {
            var g = KE.g[id];
            var html = this.execGetHtmlHooks(id, srcData);
            html = html.replace(/^\s*<br[^>]*>\s*$/ig, '');
            html = html.replace(/^\s*<p>\s*&nbsp;\s*<\/p>\s*$/ig, '');
            if (g.filterMode) {
                return KE.format.getHtml(html, g.htmlTags, g.urlType);
            } else {
                return KE.format.getHtml(html, null, g.urlType);
            }
        },
        getData: function (id, wyswygMode) {
            var g = KE.g[id];
            wyswygMode = (wyswygMode === undefined) ? g.wyswygMode : wyswygMode;
            if (!wyswygMode) {
                this.innerHtml(g.iframeDoc.body, KE.util.execSetHtmlHooks(id, g.newTextarea.value));
            }
            return this.toData(id, g.iframeDoc.body.innerHTML);
        },
        getSrcData: function (id) {
            var g = KE.g[id];
            if (!g.wyswygMode) {
                this.innerHtml(g.iframeDoc.body, KE.util.execSetHtmlHooks(id, g.newTextarea.value));
            }
            return g.iframeDoc.body.innerHTML;
        },
        getPureData: function (id) {
            return this.extractText(this.getData(id));
        },
        extractText: function (str) {
            str = str.replace(/<(?!img|embed).*?>/ig, '');
            str = str.replace(/&nbsp;/ig, ' ');
            return str;
        },
        isEmpty: function (id) {
            return this.getPureData(id).replace(/\r\n|\n|\r/, '').replace(/^\s+|\s+$/, '') === '';
        },
        setData: function (id) {
            var g = KE.g[id];
            if (g.srcTextarea) g.srcTextarea.value = this.getData(id);
        },
        focus: function (id) {
            var g = KE.g[id];
            if (g.wyswygMode) {
                g.iframeWin.focus();
            } else {
                g.newTextarea.focus();
            }
        },
        click: function (id, cmd) {
            this.focus(id);
            KE.hideMenu(id);
            KE.plugin[cmd].click(id);
        },
        selection: function (id) {
            if (!KE.browser.IE || !KE.g[id].keRange) {
                this.setSelection(id);
            }
        },
        setSelection: function (id) {
            var g = KE.g[id];
            var keSel = new KE.selection(g.iframeDoc);
            if (!KE.browser.IE || keSel.range.item || keSel.range.parentElement().ownerDocument === g.iframeDoc) {
                g.keSel = keSel;
                g.keRange = g.keSel.keRange;
                g.sel = g.keSel.sel;
                g.range = g.keSel.range;
            }
        },
        select: function (id) {
            if (KE.browser.IE && KE.g[id].wyswygMode && KE.g[id].range) KE.g[id].range.select();
        },
        execCommand: function (id, cmd, value) {
            KE.util.focus(id);
            KE.util.select(id);
            try {
                KE.g[id].iframeDoc.execCommand(cmd, false, value);
            } catch (e) { }
            KE.toolbar.updateState(id);
            KE.util.execOnchangeHandler(id);
        },
        innerHtml: function (el, html) {
            if (KE.browser.IE) {
                el.innerHTML = '<img id="__ke_temp_tag__" width="0" height="0" />' + html;
                var temp = KE.$('__ke_temp_tag__', el.ownerDocument);
                if (temp) temp.parentNode.removeChild(temp);
            } else {
                el.innerHTML = html;
            }
        },
        pasteHtml: function (id, html, isStart) {
            var g = KE.g[id];
            var imgStr = '<img id="__ke_temp_tag__" width="0" height="0" />';
            if (isStart) html = imgStr + html;
            else html += imgStr;
            if (KE.browser.IE) {
                if (g.range.item) g.range.item(0).outerHTML = html;
                else g.range.pasteHTML(html);
            } else {
                g.range.deleteContents();
                var frag = g.range.createContextualFragment(html);
                g.range.insertNode(frag);
            }
            var node = KE.$('__ke_temp_tag__', g.iframeDoc);
            var blank = g.iframeDoc.createTextNode('');
            node.parentNode.replaceChild(blank, node);
            g.keRange.selectNode(blank);
            g.keSel.addRange(g.keRange);
        },
        insertHtml: function (id, html) {
            if (html === '') return;
            var g = KE.g[id];
            if (!g.wyswygMode) return;
            if (!g.range) return;
            html = this.execSetHtmlHooks(id, html);
            if (KE.browser.IE) {
                this.select(id);
                if (g.range.item) {
                    try {
                        g.range.item(0).outerHTML = html;
                    } catch (e) {
                        var el = g.range.item(0);
                        var parent = el.parentNode;
                        parent.removeChild(el);
                        if (parent.nodeName.toLowerCase() != 'body') parent = parent.parentNode;
                        this.innerHtml(parent, html + parent.innerHTML);
                    }
                } else {
                    g.range.pasteHTML('<span id="__ke_temp_tag__">\u200B</span>' + html);
                    var node = KE.$('__ke_temp_tag__', g.iframeDoc);
                    node.parentNode.removeChild(node);
                }
            } else if (KE.browser.GECKO) {
                this.execCommand(id, 'inserthtml', html);
                return;
            } else {
                this.pasteHtml(id, html);
            }
            KE.util.execOnchangeHandler(id);
        },
        setFullHtml: function (id, html) {
            var g = KE.g[id];
            if (!KE.browser.IE && html === '') html = '<br />';
            var html = KE.util.execSetHtmlHooks(id, html);
            this.innerHtml(g.iframeDoc.body, html);
            if (!g.wyswygMode) g.newTextarea.value = KE.util.getData(id, true);
            KE.util.execOnchangeHandler(id);
        },
        selectImageWebkit: function (id, e, isSelection) {
            if (KE.browser.WEBKIT) {
                var target = e.srcElement || e.target;
                if (target.tagName.toLowerCase() == 'img') {
                    if (isSelection) KE.util.selection(id);
                    var range = KE.g[id].keRange;
                    range.selectNode(target);
                    KE.g[id].keSel.addRange(range);
                }
            }
        },
        addTabEvent: function (id) {
            var g = KE.g[id];
            KE.event.add(g.iframeDoc, 'keydown', function (e) {
                if (e.keyCode == 9) {
                    if (g.afterTab) g.afterTab(id);
                    KE.event.stop(e);
                    return false;
                }
            }, id);
        },
        addContextmenuEvent: function (id) {
            var g = KE.g[id];
            if (g.contextmenuItems.length == 0) return;
            if (!g.useContextmenu) return;
            KE.event.add(g.iframeDoc, 'contextmenu', function (e) {
                KE.hideMenu(id);
                KE.util.setSelection(id);
                KE.util.selectImageWebkit(id, e, false);
                var maxWidth = 0;
                var items = [];
                for (var i = 0, len = g.contextmenuItems.length; i < len; i++) {
                    var item = g.contextmenuItems[i];
                    if (item === '-') {
                        items.push(item);
                    } else if (item.cond && item.cond(id)) {
                        items.push(item);
                        if (item.options) {
                            var width = parseInt(item.options.width) || 0;
                            if (width > maxWidth) maxWidth = width;
                        }
                    }
                    prevItem = item;
                }
                while (items.length > 0 && items[0] === '-') {
                    items.shift();
                }
                while (items.length > 0 && items[items.length - 1] === '-') {
                    items.pop();
                }
                var prevItem = null;
                for (var i = 0, len = items.length; i < len; i++) {
                    if (items[i] === '-' && prevItem === '-') delete items[i];
                    prevItem = items[i] || null;
                }
                if (items.length > 0) {
                    var menu = new KE.menu({
                        id: id,
                        event: e,
                        type: 'contextmenu',
                        width: maxWidth
                    });
                    for (var i = 0, len = items.length; i < len; i++) {
                        var item = items[i];
                        if (!item) continue;
                        if (item === '-') {
                            if (i < len - 1) menu.addSeparator();
                        } else {
                            menu.add(item.text, (function (item) {
                                return function () {
                                    item.click(id, menu);
                                };
                            })(item), item.options);
                        }
                    }
                    menu.show();
                    KE.event.stop(e);
                    return false;
                }
                return true;
            }, id);
        },
        addNewlineEvent: function (id) {
            var g = KE.g[id];
            if (KE.browser.IE && g.newlineTag.toLowerCase() != 'br') return;
            if (KE.browser.GECKO && KE.browser.VERSION < 3 && g.newlineTag.toLowerCase() != 'p') return;
            if (KE.browser.OPERA) return;
            KE.event.add(g.iframeDoc, 'keydown', function (e) {
                if (e.keyCode != 13 || e.shiftKey || e.ctrlKey || e.altKey) return true;
                KE.util.setSelection(id);
                var parent = g.keRange.getParentElement();
                if (KE.util.inMarquee(parent)) return;
                var tagName = parent.tagName.toLowerCase();
                if (g.newlineTag.toLowerCase() == 'br') {
                    if (!KE.util.inArray(tagName, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li'])) {
                        KE.util.pasteHtml(id, '<br />');
                        var nextNode = g.keRange.startNode.nextSibling;
                        if (KE.browser.IE) {
                            if (!nextNode) KE.util.pasteHtml(id, '<br />', true);
                        } else if (KE.browser.WEBKIT) {
                            if (!nextNode) {
                                KE.util.pasteHtml(id, '<br />', true);
                            } else {
                                var range = new KE.range(g.iframeDoc);
                                range.selectNode(nextNode.parentNode);
                                range.setStart(nextNode, 0);
                                if (range.cloneContents().innerHTML.replace(/<(?!img|embed).*?>/ig, '') === '') {
                                    KE.util.pasteHtml(id, '<br />', true);
                                }
                            }
                        }
                        KE.event.stop(e);
                        return false;
                    }
                } else {
                    if (!KE.util.inArray(tagName, ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'div', 'li'])) {
                        KE.util.execCommand(id, 'formatblock', '<P>');
                    }
                }
                return true;
            }, id);
        }
    };

    KE.layout = {
        hide: function (id) {
            var g = KE.g[id];
            KE.hideMenu(id);
            var stack = g.dialogStack;
            while (stack.length > 0) {
                var dialog = stack[stack.length - 1];
                dialog.hide();
            }
            g.maskDiv.style.display = 'none';
        }
    };

    KE.hideMenu = function (id) {
        var g = KE.g[id];
        g.hideDiv.innerHTML = '';
        g.hideDiv.style.display = 'none';
    };

    KE.colorpicker = function (arg) {
        var wrapper;
        var x = arg.x || 0;
        var y = arg.y || 0;
        var z = arg.z || 0;
        var colors = arg.colors || KE.setting.colorTable;
        var doc = arg.doc || document;
        var onclick = arg.onclick;
        var selectedColor = (arg.selectedColor || '').toLowerCase();
        function init() {
            wrapper = KE.$$('div');
            wrapper.className = 'ke-colorpicker';
            wrapper.style.top = y + 'px';
            wrapper.style.left = x + 'px';
            wrapper.style.zIndex = z;
        }
        init.call(this);
        this.remove = function () {
            doc.body.removeChild(wrapper);
        };
        this.getElement = function () {
            function addAttr(cell, color, cls) {
                if (selectedColor === color.toLowerCase()) cls += ' ke-colorpicker-cell-selected';
                cell.className = cls;
                cell.title = color || KE.lang['noColor'];
                cell.onmouseover = function () { this.className = cls + ' ke-colorpicker-cell-on'; };
                cell.onmouseout = function () { this.className = cls; };
                cell.onclick = function () { onclick(color); };
                if (color) {
                    var div = KE.$$('div');
                    div.className = 'ke-colorpicker-cell-color';
                    div.style.backgroundColor = color;
                    cell.appendChild(div);
                } else {
                    cell.innerHTML = KE.lang['noColor'];
                }
            }
            var table = KE.$$('table');
            table.className = 'ke-colorpicker-table';
            table.cellPadding = 0;
            table.cellSpacing = 0;
            table.border = 0;
            var row = table.insertRow(0),
			cell = row.insertCell(0);
            cell.colSpan = colors[0].length;
            addAttr(cell, '', 'ke-colorpicker-cell-top');
            for (var i = 0; i < colors.length; i++) {
                var row = table.insertRow(i + 1);
                for (var j = 0; j < colors[i].length; j++) {
                    var color = colors[i][j],
					cell = row.insertCell(j);
                    addAttr(cell, color, 'ke-colorpicker-cell');
                }
            }
            return table;
        };
        this.create = function () {
            wrapper.appendChild(this.getElement());
            KE.event.bind(wrapper, 'click', function (e) { });
            KE.event.bind(wrapper, 'mousedown', function (e) { });
            doc.body.appendChild(wrapper);
        };
    };

    KE.menu = function (arg) {
        function getPos(width, height) {
            var id = arg.id;
            var x = 0;
            var y = 0;
            if (this.type == 'menu') {
                var obj = KE.g[id].toolbarIcon[arg.cmd];
                var pos = KE.util.getElementPos(obj[0]);
                x = pos.x;
                y = pos.y + obj[0].offsetHeight;
            } else {
                var pos = KE.util.getCoords(arg.event);
                var iframePos = KE.util.getElementPos(KE.g[id].iframe);
                x = pos.x + iframePos.x;
                y = pos.y + iframePos.y + 5;
            }
            if (width > 0 || height > 0) {
                var scrollPos = KE.util.getScrollPos();
                var docEl = KE.util.getDocumentElement();
                var maxLeft = scrollPos.x + docEl.clientWidth - width - 2;
                if (x > maxLeft) x = maxLeft;
            }
            return { x: x, y: y };
        };
        function init() {
            var width = arg.width;
            this.type = (arg.type && arg.type == 'contextmenu') ? arg.type : 'menu';
            var div = KE.$$('div');
            div.className = 'ke-' + this.type;
            div.setAttribute('name', arg.cmd);
            var pos = getPos.call(this, 0, 0);
            div.style.top = pos.y + 'px';
            div.style.left = pos.x + 'px';
            if (arg.width) div.style.width = (/^\d+$/.test(width)) ? width + 'px' : width;
            KE.event.bind(div, 'click', function (e) { }, arg.id);
            KE.event.bind(div, 'mousedown', function (e) { }, arg.id);
            this.div = div;
        };
        init.call(this);
        this.add = function (html, event, options) {
            var height, iconHtml, checked = false;
            if (options !== undefined) {
                height = options.height;
                iconHtml = options.iconHtml;
                checked = options.checked;
            }
            var self = this;
            var cDiv = KE.$$('div');
            cDiv.className = 'ke-' + self.type + '-item';
            if (height) cDiv.style.height = height;
            var left = KE.$$('div');
            left.className = 'ke-' + this.type + '-left';
            var center = KE.$$('div');
            center.className = 'ke-' + self.type + '-center';
            if (height) center.style.height = height;
            var right = KE.$$('div');
            right.className = 'ke-' + this.type + '-right';
            if (height) right.style.lineHeight = height;
            cDiv.onmouseover = function () {
                this.className = 'ke-' + self.type + '-item ke-' + self.type + '-item-on';
                center.className = 'ke-' + self.type + '-center ke-' + self.type + '-center-on';
            };
            cDiv.onmouseout = function () {
                this.className = 'ke-' + self.type + '-item';
                center.className = 'ke-' + self.type + '-center';
            };
            cDiv.onclick = event;
            cDiv.appendChild(left);
            cDiv.appendChild(center);
            cDiv.appendChild(right);
            if (checked) {
                KE.util.innerHtml(left, '<span class="ke-common-icon ke-common-icon-url ke-icon-checked"></span>');
            } else {
                if (iconHtml) KE.util.innerHtml(left, iconHtml);
            }
            KE.util.innerHtml(right, html);
            this.append(cDiv);
        };
        this.addSeparator = function () {
            var div = KE.$$('div');
            div.className = 'ke-' + this.type + '-separator';
            this.append(div);
        };
        this.append = function (el) {
            this.div.appendChild(el);
        };
        this.insert = function (html) {
            KE.util.innerHtml(this.div, html);
        };
        this.hide = function () {
            KE.hideMenu(arg.id);
        };
        this.show = function () {
            this.hide();
            var id = arg.id;
            KE.g[id].hideDiv.style.display = '';
            KE.g[id].hideDiv.appendChild(this.div);
            var pos = getPos.call(this, this.div.clientWidth, this.div.clientHeight);
            this.div.style.top = pos.y + 'px';
            this.div.style.left = pos.x + 'px';
        };
        this.picker = function (color) {
            var colorTable = KE.g[arg.id].colorTable;
            var picker = new KE.colorpicker({
                colors: colorTable,
                onclick: function (color) { KE.plugin[arg.cmd].exec(arg.id, color); },
                selectedColor: color
            });
            this.append(picker.getElement());
            this.show();
        };
    };

    KE.button = function (arg) {
        arg = arg || {};
        doc = arg.doc || document;
        var span = KE.$$('span', doc);
        span.className = 'ke-button-common ke-button-outer ' + (arg.className || '');
        span.title = arg.text;
        btn = KE.$$('input', doc);
        btn.className = 'ke-button-common ke-button';
        btn.type = 'button';
        btn.value = arg.text || '';
        if (arg.clickFn) {
            btn.onclick = arg.clickFn;
        }
        span.appendChild(btn);
        return { span: span, btn: btn };
    }

    KE.dialog = function (arg) {
        var self = this;
        this.widthMargin = 30;
        this.heightMargin = 100;
        this.zIndex = 19811214;
        this.width = arg.width;
        this.height = arg.height;
        var minTop, minLeft;
        function setLimitNumber() {
            var docEl = KE.util.getDocumentElement();
            var pos = KE.util.getScrollPos();
            minTop = pos.y;
            minLeft = pos.x;
        }
        function init() {
            this.beforeHide = arg.beforeHide;
            this.afterHide = arg.afterHide;
            this.beforeShow = arg.beforeShow;
            this.afterShow = arg.afterShow;
            this.ondrag = arg.ondrag;
        }
        init.call(this);
        function getPos() {
            var width = this.width + this.widthMargin;
            var height = this.height + this.heightMargin;
            var id = arg.id;
            var g = KE.g[id];
            var x = 0, y = 0;
            if (g.dialogAlignType == 'page') {
                var el = KE.util.getDocumentElement();
                var scrollPos = KE.util.getScrollPos();
                x = Math.round(scrollPos.x + (el.clientWidth - width) / 2);
                y = Math.round(scrollPos.y + (el.clientHeight - height) / 2);
            } else {
                var pos = KE.util.getElementPos(KE.g[id].container);
                var el = g.container;
                var xDiff = Math.round(el.clientWidth / 2) - Math.round(width / 2);
                var yDiff = Math.round(el.clientHeight / 2) - Math.round(height / 2);
                x = xDiff < 0 ? pos.x : pos.x + xDiff;
                y = yDiff < 0 ? pos.y : pos.y + yDiff;
            }
            x = x < 0 ? 0 : x;
            y = y < 0 ? 0 : y;
            return { x: x, y: y };
        };
        this.resize = function (width, height) {
            if (width) this.width = width;
            if (height) this.height = height;
            this.hide();
            this.show();
        };
        this.hide = function () {
            if (this.beforeHide) this.beforeHide(id);
            var id = arg.id;
            var stack = KE.g[id].dialogStack;
            if (stack[stack.length - 1] != this) return;
            var dialog = stack.pop();
            var iframe = dialog.iframe;
            iframe.src = 'javascript:false';
            iframe.parentNode.removeChild(iframe);
            document.body.removeChild(this.div);
            if (stack.length < 1) {
                KE.g[id].maskDiv.style.display = 'none';
            }
            KE.event.remove(window, 'resize', setLimitNumber);
            KE.event.remove(window, 'scroll', setLimitNumber);
            if (this.afterHide) this.afterHide(id);
            KE.util.focus(id);
        };
        this.show = function () {
            if (this.beforeShow) this.beforeShow(id);
            var self = this;
            var id = arg.id;
            var div = KE.$$('div');
            div.className = 'ke-dialog';
            KE.event.bind(div, 'click', function (e) { }, id);
            var stack = KE.g[id].dialogStack;
            if (stack.length > 0) {
                this.zIndex = stack[stack.length - 1].zIndex + 1;
            }
            div.style.zIndex = this.zIndex;
            var pos = getPos.call(this);
            div.style.top = pos.y + 'px';
            div.style.left = pos.x + 'px';
            if (KE.g[id].shadowMode) {
                KE.addClass(div, 'ke-dialog-shadow');
            } else {
                KE.addClass(div, 'ke-dialog-no-shadow');
            }
            var titleDiv = KE.$$('div');
            titleDiv.className = 'ke-dialog-title';
            titleDiv.innerHTML = arg.title;
            var span = KE.$$('span');
            span.className = 'ke-dialog-close';
            span.alt = KE.lang['close'];
            span.title = KE.lang['close'];
            span.onclick = function () {
                self.hide();
                KE.util.select(id);
            };
            titleDiv.appendChild(span);
            setLimitNumber();
            KE.event.add(window, 'resize', setLimitNumber);
            KE.event.add(window, 'scroll', setLimitNumber);
            KE.util.drag(id, titleDiv, div, function (objTop, objLeft, objWidth, objHeight, top, left) {
                if (self.ondrag) self.ondrag(id);
                setLimitNumber();
                top = objTop + top;
                left = objLeft + left;
                if (top < minTop) top = minTop;
                if (left < minLeft) left = minLeft;
                div.style.top = top + 'px';
                div.style.left = left + 'px';
            });
            div.appendChild(titleDiv);
            var bodyDiv = KE.$$('div');
            bodyDiv.className = 'ke-dialog-body';
            var loadingTable = KE.util.createTable();
            loadingTable.table.className = 'ke-loading-table';
            loadingTable.table.style.width = this.width + 'px';
            loadingTable.table.style.height = this.height + 'px';
            var loadingImg = KE.$$('span');
            loadingImg.className = 'ke-loading-img';
            loadingTable.cell.appendChild(loadingImg);
            var iframe = (KE.g[id].dialogStack.length == 0 && KE.g[id].dialog) ? KE.g[id].dialog : KE.$$('iframe');
            if (arg.useFrameCSS) {
                iframe.className = 'ke-dialog-iframe ke-dialog-iframe-border';
            } else {
                iframe.className = 'ke-dialog-iframe';
            }
            iframe.setAttribute("frameBorder", "0");
            iframe.style.width = this.width + 'px';
            iframe.style.height = this.height + 'px';
            iframe.style.display = 'none';
            bodyDiv.appendChild(iframe);
            bodyDiv.appendChild(loadingTable.table);
            div.appendChild(bodyDiv);
            var bottomDiv = KE.$$('div');
            bottomDiv.className = 'ke-dialog-bottom';
            var noButton = null;
            var yesButton = null;
            var previewButton = null;
            if (arg.previewButton) {
                var btn = KE.button({
                    className: 'ke-dialog-preview',
                    text: arg.previewButton,
                    clickFn: function () {
                        var stack = KE.g[id].dialogStack;
                        if (stack[stack.length - 1] == self) {
                            if (arg.previewClickFn) {
                                arg.previewClickFn(id);
                            } else {
                                KE.plugin[arg.cmd].preview(id);
                            }
                        }
                    }
                });
                previewButton = btn.btn;
                bottomDiv.appendChild(btn.span);
            }
            if (arg.yesButton) {
                var btn = KE.button({
                    className: 'ke-dialog-yes',
                    text: arg.yesButton,
                    clickFn: function () {
                        var stack = KE.g[id].dialogStack;
                        if (stack[stack.length - 1] == self) {
                            if (arg.yesClickFn) {
                                arg.yesClickFn(id);
                            } else {
                                KE.plugin[arg.cmd].exec(id);
                            }
                        }
                    }
                });
                yesButton = btn.btn;
                bottomDiv.appendChild(btn.span);
            }
            if (arg.noButton) {
                var btn = KE.button({
                    className: 'ke-dialog-no',
                    text: arg.noButton,
                    clickFn: function () {
                        self.hide();
                        KE.util.select(id);
                    }
                });
                noButton = btn.btn;
                bottomDiv.appendChild(btn.span);
            }
            if (arg.yesButton || arg.noButton || arg.previewButton) {
                div.appendChild(bottomDiv);
            }
            document.body.appendChild(div);
            KE.event.bind(div, 'mousedown', function (e) { }, id);
            window.focus();
            if (arg.html !== undefined) {
                var dialogDoc = KE.util.getIframeDoc(iframe);
                var html = KE.util.getFullHtml(id);
                dialogDoc.open();
                dialogDoc.write(html);
                dialogDoc.close();
                KE.util.innerHtml(dialogDoc.body, arg.html);
            } else if (arg.url !== undefined) {
                iframe.src = arg.url;
            } else {
                var param = 'id=' + escape(id) + '&ver=' + escape(KE.version);
                if (arg.file === undefined) {
                    iframe.src = KE.g[id].pluginsPath + arg.cmd + '.html?' + param;
                } else {
                    param = (/\?/.test(arg.file) ? '&' : '?') + param;
                    iframe.src = KE.g[id].pluginsPath + arg.file + param;
                }
            }
            KE.g[id].maskDiv.style.width = KE.util.getDocumentWidth() + 'px';
            KE.g[id].maskDiv.style.height = KE.util.getDocumentHeight() + 'px';
            KE.g[id].maskDiv.style.display = 'block';
            this.iframe = iframe;
            this.loading = loadingTable.table;
            this.noButton = noButton;
            this.yesButton = yesButton;
            this.previewButton = previewButton;
            this.div = div;
            KE.g[id].dialogStack.push(this);
            KE.g[id].dialog = iframe;
            KE.g[id].yesButton = yesButton;
            KE.g[id].noButton = noButton;
            KE.g[id].previewButton = previewButton;
            if (!arg.loadingMode) KE.util.hideLoadingPage(id);
            if (this.afterShow) this.afterShow(id);
            if (KE.g[id].afterDialogCreate) KE.g[id].afterDialogCreate(id);
        };
    };

    KE.toolbar = {
        updateState: function (id) {
            var cmdList = [
			'justifyleft', 'justifycenter', 'justifyright',
			'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript', 'superscript',
			'bold', 'italic', 'underline', 'strikethrough'
		];
            for (var i = 0; i < cmdList.length; i++) {
                var cmd = cmdList[i];
                var state = false;
                try {
                    state = KE.g[id].iframeDoc.queryCommandState(cmd);
                } catch (e) { }
                if (state) {
                    KE.toolbar.select(id, cmd);
                } else {
                    KE.toolbar.unselect(id, cmd);
                }
            }
        },
        isSelected: function (id, cmd) {
            if (KE.plugin[cmd] && KE.plugin[cmd].isSelected) return true;
            else return false;
        },
        select: function (id, cmd) {
            if (KE.g[id].toolbarIcon[cmd]) {
                var a = KE.g[id].toolbarIcon[cmd][0];
                a.className = 'ke-icon ke-icon-selected';
                a.onmouseover = null;
                a.onmouseout = null;
            }
        },
        unselect: function (id, cmd) {
            if (KE.g[id].toolbarIcon[cmd]) {
                var a = KE.g[id].toolbarIcon[cmd][0];
                a.className = 'ke-icon';
                a.onmouseover = function () { this.className = 'ke-icon ke-icon-on'; };
                a.onmouseout = function () { this.className = 'ke-icon'; };
            }
        },
        _setAttr: function (id, a, cmd) {
            a.className = 'ke-icon';
            a.href = 'javascript:;';
            a.onclick = function (e) {
                e = e || window.event;
                var div = KE.g[id].hideDiv.firstChild;
                if (div && div.getAttribute('name') == cmd) {
                    KE.hideMenu(id);
                } else {
                    KE.util.click(id, cmd);
                }
                if (e.preventDefault) e.preventDefault();
                if (e.stopPropagation) e.stopPropagation();
                if (e.cancelBubble !== undefined) e.cancelBubble = true;
                return false;
            };
            a.onmouseover = function () { this.className = 'ke-icon ke-icon-on'; };
            a.onmouseout = function () { this.className = 'ke-icon'; };
            a.hidefocus = true;
            a.title = KE.lang[cmd];
        },
        able: function (id, arr) {
            var self = this;
            KE.each(KE.g[id].toolbarIcon, function (cmd, obj) {
                if (!KE.util.inArray(cmd, arr)) {
                    var a = obj[0];
                    var span = obj[1];
                    self._setAttr(id, a, cmd);
                    KE.util.setOpacity(span, 100);
                }
            });
        },
        disable: function (id, arr) {
            KE.each(KE.g[id].toolbarIcon, function (cmd, obj) {
                if (!KE.util.inArray(cmd, arr)) {
                    var a = obj[0];
                    var span = obj[1];
                    a.className = 'ke-icon ke-icon-disabled';
                    KE.util.setOpacity(span, 50);
                    a.onclick = null;
                    a.onmouseover = null;
                    a.onmouseout = null;
                }
            });
        },
        create: function (id) {
            var self = this;
            var defaultItemHash = KE.util.arrayToHash(KE.setting.items);
            KE.g[id].toolbarIcon = [];
            var tableObj = KE.util.createTable();
            var toolbar = tableObj.table;
            toolbar.className = 'ke-toolbar';
            toolbar.oncontextmenu = function () { return false; };
            toolbar.onmousedown = function () { return false; };
            toolbar.onmousemove = function () { return false; };
            var toolbarCell = tableObj.cell;
            var length = KE.g[id].items.length;
            var cellNum = 0;
            var row;
            KE.g[id].toolbarHeight = KE.g[id].toolbarLineHeight;
            for (var i = 0; i < length; i++) {
                var cmd = KE.g[id].items[i];
                if (i == 0 || cmd == '-') {
                    var table = KE.$$('table');
                    table.cellPadding = 0;
                    table.cellSpacing = 0;
                    table.border = 0;
                    table.className = 'ke-toolbar-table';
                    row = table.insertRow(0);
                    cellNum = 0;
                    toolbarCell.appendChild(table);
                    if (cmd == '-') {
                        KE.g[id].toolbarHeight += KE.g[id].toolbarLineHeight;
                        continue;
                    }
                }
                var cell = row.insertCell(cellNum);
                cell.hideforcus = true;
                cellNum++;
                if (cmd == '|') {
                    var div = KE.$$('div');
                    div.className = 'ke-toolbar-separator';
                    cell.appendChild(div);
                    continue;
                }
                var a = KE.$$('a');
                a.tabIndex = -1;
                self._setAttr(id, a, cmd);
                var span = KE.$$('span');
                if (typeof defaultItemHash[cmd] == 'undefined') {
                    span.className = 'ke-common-icon ke-icon-' + cmd;
                } else {
                    span.className = 'ke-common-icon ke-common-icon-url ke-icon-' + cmd;
                }
                a.appendChild(span);
                cell.appendChild(a);
                KE.g[id].toolbarIcon[cmd] = [a, span];
                if (KE.toolbar.isSelected(id, cmd)) KE.toolbar.select(id, cmd);
            }
            return toolbar;
        }
    };

    KE.history = {
        addStackData: function (stack, data) {
            var prev = '';
            if (stack.length > 0) {
                prev = stack[stack.length - 1];
            }
            if (stack.length == 0 || data !== prev) stack.push(data);
        },
        add: function (id, minChangeSize) {
            var g = KE.g[id];
            var html = KE.util.getSrcData(id);
            if (g.undoStack.length > 0) {
                var prevHtml = g.undoStack[g.undoStack.length - 1];
                if (Math.abs(html.length - prevHtml.length) < minChangeSize) return;
            }
            this.addStackData(g.undoStack, html);
        },
        undo: function (id) {
            var g = KE.g[id];
            if (g.undoStack.length == 0) return;
            var html = KE.util.getSrcData(id);
            this.addStackData(g.redoStack, html);
            var prevHtml = g.undoStack.pop();
            if (html === prevHtml && g.undoStack.length > 0) {
                prevHtml = g.undoStack.pop();
            }
            prevHtml = KE.util.toData(id, prevHtml);
            if (g.wyswygMode) {
                KE.util.innerHtml(g.iframeDoc.body, KE.util.execSetHtmlHooks(id, prevHtml));
            } else {
                g.newTextarea.value = prevHtml;
            }
        },
        redo: function (id) {
            var g = KE.g[id];
            if (g.redoStack.length == 0) return;
            var html = KE.util.getSrcData(id);
            this.addStackData(g.undoStack, html);
            var nextHtml = g.redoStack.pop();
            nextHtml = KE.util.toData(id, nextHtml);
            if (g.wyswygMode) {
                KE.util.innerHtml(g.iframeDoc.body, KE.util.execSetHtmlHooks(id, nextHtml));
            } else {
                g.newTextarea.value = nextHtml;
            }
        }
    };

    KE.readonly = function (id, isReadonly) {
        isReadonly = isReadonly == undefined ? true : isReadonly;
        var g = KE.g[id];
        if (KE.browser.IE) g.iframeDoc.body.contentEditable = isReadonly ? 'false' : 'true';
        else g.iframeDoc.designMode = isReadonly ? 'off' : 'on';
    };

    KE.focus = function (id, position) {
        position = (position || '').toLowerCase();
        if (!KE.g[id].container) return;
        KE.util.focus(id);
        if (position === 'end') {
            KE.util.setSelection(id);
            if (!KE.g[id].sel) return; //issue #120: Sometimes Firefox does not get selection
            var sel = KE.g[id].keSel,
			range = KE.g[id].keRange,
			doc = KE.g[id].iframeDoc;
            range.selectTextNode(doc.body);
            range.collapse(false);
            sel.addRange(range);
        }
    };

    KE.blur = function (id) {
        var g = KE.g[id];
        if (!g.container) return;
        if (KE.browser.IE) {
            var input = KE.$$('input');
            input.type = 'text';
            g.container.appendChild(input);
            input.focus();
            g.container.removeChild(input);
        } else {
            g.wyswygMode ? g.iframeWin.blur() : g.newTextarea.blur();
        }
    };

    KE.html = function (id, val) {
        if (val === undefined) {
            return KE.util.getData(id);
        } else {
            if (!KE.g[id].container) return;
            KE.util.setFullHtml(id, val);
            KE.focus(id);
        }
    };

    KE.text = function (id, val) {
        if (val === undefined) {
            val = KE.html(id);
            val = val.replace(/<.*?>/ig, '');
            val = val.replace(/&nbsp;/ig, ' ');
            val = KE.util.trim(val);
            return val;
        } else {
            KE.html(id, KE.util.escape(val));
        }
    };

    KE.insertHtml = function (id, val) {
        if (!KE.g[id].container) return;
        var range = KE.g[id].range;
        if (!range) {
            KE.appendHtml(id, val);
        } else {
            KE.focus(id);
            KE.util.selection(id);
            KE.util.insertHtml(id, val);
        }
    };

    KE.appendHtml = function (id, val) {
        KE.html(id, KE.html(id) + val);
    };

    KE.isEmpty = function (id) {
        return KE.util.isEmpty(id);
    };

    KE.selectedHtml = function (id) {
        var range = KE.g[id].range;
        if (!range) return '';
        var html = '';
        if (KE.browser.IE) {
            if (range.item) {
                html = range.item(0).outerHTML;
            } else {
                html = range.htmlText;
            }
        } else {
            var temp = KE.$$('div', KE.g[id].iframeDoc);
            temp.appendChild(range.cloneContents());
            html = temp.innerHTML;
        }
        return KE.util.toData(id, html);
    };

    KE.count = function (id, mode) {
        mode = (mode || 'html').toLowerCase();
        if (mode === 'html') {
            return KE.html(id).length;
        } else if (mode === 'text') {
            var data = KE.util.getPureData(id);
            data = data.replace(/<(?:img|embed).*?>/ig, 'K');
            data = data.replace(/\r\n|\n|\r/g, '');
            data = KE.util.trim(data);
            return data.length;
        }
        return 0;
    };

    KE.sync = function (id) {
        return KE.util.setData(id);
    };

    KE.remove = function (id, mode) {
        var g = KE.g[id];
        if (!g.container) return false;
        mode = (typeof mode == "undefined") ? 0 : mode;
        KE.util.setData(id);
        var container = g.container;
        var eventStack = g.eventStack;
        for (var i = 0, len = eventStack.length; i < len; i++) {
            var item = eventStack[i];
            if (item) KE.event.remove(item.el, item.type, item.fn, id);
        }
        g.iframeDoc.src = 'javascript:false';
        g.iframe.parentNode.removeChild(g.iframe);
        if (mode == 1) {
            document.body.removeChild(container);
        } else {
            var srcTextarea = g.srcTextarea;
            srcTextarea.parentNode.removeChild(container);
            if (mode == 0) srcTextarea.style.display = '';
        }
        document.body.removeChild(g.hideDiv);
        document.body.removeChild(g.maskDiv);
        g.container = null;
        g.dialogStack = [];
        g.contextmenuItems = [];
        g.getHtmlHooks = [];
        g.setHtmlHooks = [];
        g.onchangeHandlerStack = [];
        g.eventStack = [];
    };

    KE.create = function (id, mode) {
        if (KE.g[id].beforeCreate) KE.g[id].beforeCreate(id);
        if (KE.browser.IE && KE.browser.VERSION < 7) try { document.execCommand('BackgroundImageCache', false, true); } catch (e) { }
        var srcTextarea = KE.$(id) || document.getElementsByName(id)[0];
        mode = (typeof mode == "undefined") ? 0 : mode;
        if (mode == 0 && KE.g[id].container) return;
        var width = KE.g[id].width || srcTextarea.style.width || srcTextarea.offsetWidth + 'px';
        var height = KE.g[id].height || srcTextarea.style.height || srcTextarea.offsetHeight + 'px';
        var tableObj = KE.util.createTable();
        var container = tableObj.table;
        container.className = 'ke-container';
        container.style.width = width;
        container.style.height = height;
        var toolbarOuter = tableObj.cell;
        toolbarOuter.className = 'ke-toolbar-outer';
        var textareaOuter = container.insertRow(1).insertCell(0);
        textareaOuter.className = 'ke-textarea-outer';
        tableObj = KE.util.createTable();
        var textareaTable = tableObj.table;
        textareaTable.className = 'ke-textarea-table';
        var textareaCell = tableObj.cell;
        textareaOuter.appendChild(textareaTable);
        var bottomOuter = container.insertRow(2).insertCell(0);
        bottomOuter.className = 'ke-bottom-outer';
        srcTextarea.style.display = 'none';
        if (mode == 1) document.body.appendChild(container);
        else srcTextarea.parentNode.insertBefore(container, srcTextarea);
        var toolbarTable = KE.toolbar.create(id);
        toolbarTable.style.height = KE.g[id].toolbarHeight + 'px';
        toolbarOuter.appendChild(toolbarTable);
        var iframe = KE.g[id].iframe || KE.$$('iframe');
        iframe.tabIndex = KE.g[id].tabIndex || srcTextarea.tabIndex;
        iframe.className = 'ke-iframe';
        iframe.setAttribute("frameBorder", "0");
        var newTextarea = KE.$$('textarea');
        newTextarea.tabIndex = iframe.tabIndex;
        newTextarea.className = 'ke-textarea';
        newTextarea.style.display = 'none';
        KE.g[id].container = container;
        KE.g[id].iframe = iframe;
        KE.g[id].newTextarea = newTextarea;
        KE.util.resize(id, width, height);
        textareaCell.appendChild(iframe);
        textareaCell.appendChild(newTextarea);
        var bottom = KE.$$('table');
        bottom.className = 'ke-bottom';
        bottom.cellPadding = 0;
        bottom.cellSpacing = 0;
        bottom.border = 0;
        bottom.style.height = KE.g[id].statusbarHeight + 'px';
        var row = bottom.insertRow(0);
        var bottomLeft = row.insertCell(0);
        bottomLeft.className = 'ke-bottom-left';
        var leftImg = KE.$$('span');
        leftImg.className = 'ke-bottom-left-img';
        if (KE.g[id].config.resizeMode == 0 || mode == 1) {
            bottomLeft.style.cursor = 'default';
            leftImg.style.visibility = 'hidden';
        }
        bottomLeft.appendChild(leftImg);
        var bottomRight = row.insertCell(1);
        bottomRight.className = 'ke-bottom-right';
        var rightImg = KE.$$('span');
        rightImg.className = 'ke-bottom-right-img';
        if (KE.g[id].config.resizeMode == 0 || mode == 1) {
            bottomRight.style.cursor = 'default';
            rightImg.style.visibility = 'hidden';
        } else if (KE.g[id].config.resizeMode == 1) {
            bottomRight.style.cursor = 's-resize';
            rightImg.style.visibility = 'hidden';
        }
        bottomRight.appendChild(rightImg);
        bottomOuter.appendChild(bottom);
        var hideDiv = KE.$$('div');
        hideDiv.className = 'ke-reset';
        hideDiv.style.display = 'none';
        var maskDiv = KE.$$('div');
        maskDiv.className = 'ke-mask';
        KE.util.setOpacity(maskDiv, 50);
        KE.event.bind(maskDiv, 'click', function (e) { }, id);
        KE.event.bind(maskDiv, 'mousedown', function (e) { }, id);
        document.body.appendChild(hideDiv);
        document.body.appendChild(maskDiv);
        KE.util.setDefaultPlugin(id);
        var iframeWin = iframe.contentWindow;
        var iframeDoc = KE.util.getIframeDoc(iframe);
        if (!KE.browser.IE) iframeDoc.designMode = 'on';
        var html = KE.util.getFullHtml(id);
        iframeDoc.open();
        iframeDoc.write(html);
        iframeDoc.close();
        if (!KE.g[id].wyswygMode) {
            newTextarea.value = KE.util.execSetHtmlHooks(id, srcTextarea.value);
            newTextarea.style.display = 'block';
            iframe.style.display = 'none';
            KE.toolbar.disable(id, ['source', 'fullscreen']);
            KE.toolbar.select(id, 'source');
        }
        if (KE.g[id].syncType == 'form') {
            var el = srcTextarea;
            while ((el = el.parentNode)) {
                if (el.nodeName.toLowerCase() == 'form') {
                    KE.event.add(el, 'submit', function () { KE.sync(id); }, id);
                    break;
                }
            }
        }
        function hideMenu() {
            KE.hideMenu(id);
        }
        function updateToolbar() {
            KE.toolbar.updateState(id);
        }
        if (KE.browser.WEBKIT) {
            KE.event.add(iframeDoc, 'click', function (e) {
                KE.util.selectImageWebkit(id, e, true);
            }, id);
        }
        if (KE.browser.IE) {
            KE.event.add(iframeDoc, 'keydown', function (e) {
                if (e.keyCode == 8) {
                    var range = KE.g[id].range;
                    if (range.item) {
                        var item = range.item(0);
                        item.parentNode.removeChild(item);
                        KE.util.execOnchangeHandler(id);
                        KE.event.stop(id);
                        return false;
                    }
                }
            }, id);
        }
        function afterFocus() {
            if (KE.g[id].afterFocus) KE.g[id].afterFocus(id);
        }
        function afterBlur() {
            if (KE.g[id].afterBlur) KE.g[id].afterBlur(id);
        }
        KE.event.add(iframeDoc, 'mousedown', hideMenu, id);
        KE.event.add(iframeDoc, 'click', updateToolbar, id);
        KE.event.input(iframeDoc, updateToolbar, id);
        KE.event.bind(newTextarea, 'click', hideMenu, id);
        KE.event.add(document, 'click', hideMenu, id);
        KE.event.add(iframeWin, 'focus', afterFocus);
        KE.event.add(newTextarea, 'focus', afterFocus);
        KE.event.add(iframeWin, 'blur', afterBlur);
        KE.event.add(newTextarea, 'blur', afterBlur);
        KE.g[id].toolbarTable = toolbarTable;
        KE.g[id].textareaTable = textareaTable;
        KE.g[id].srcTextarea = srcTextarea;
        KE.g[id].bottom = bottom;
        KE.g[id].hideDiv = hideDiv;
        KE.g[id].maskDiv = maskDiv;
        KE.g[id].iframeWin = iframeWin;
        KE.g[id].iframeDoc = iframeDoc;
        KE.g[id].width = width;
        KE.g[id].height = height;
        KE.util.drag(id, bottomRight, container, function (objTop, objLeft, objWidth, objHeight, top, left) {
            if (KE.g[id].resizeMode == 2) KE.util.resize(id, (objWidth + left) + 'px', (objHeight + top) + 'px', true);
            else if (KE.g[id].resizeMode == 1) KE.util.resize(id, objWidth + 'px', (objHeight + top) + 'px', true, false);
        });
        KE.util.drag(id, bottomLeft, container, function (objTop, objLeft, objWidth, objHeight, top, left) {
            if (KE.g[id].resizeMode > 0) KE.util.resize(id, objWidth + 'px', (objHeight + top) + 'px', true, false);
        });
        KE.each(KE.plugin, function (cmd, plugin) {
            if (plugin.init) plugin.init(id);
        });
        KE.g[id].getHtmlHooks.push(function (html) {
            html = html.replace(/(<[^>]*)kesrc="([^"]+)"([^>]*>)/ig, function (full, start, src, end) {
                full = full.replace(/(\s+(?:href|src)=")[^"]+(")/i, '$1' + src + '$2');
                full = full.replace(/\s+kesrc="[^"]+"/i, '');
                return full;
            });
            html = html.replace(/(<[^>]+\s)ke-(on\w+="[^"]+"[^>]*>)/ig, function (full, start, end) {
                return start + end;
            });
            return html;
        });
        KE.g[id].setHtmlHooks.push(function (html) {
            html = html.replace(/(<[^>]*)(href|src)="([^"]+)"([^>]*>)/ig, function (full, start, key, src, end) {
                if (full.match(/\skesrc="[^"]+"/i)) return full;
                full = start + key + '="' + src + '"' + ' kesrc="' + src + '"' + end;
                return full;
            });
            html = html.replace(/(<[^>]+\s)(on\w+="[^"]+"[^>]*>)/ig, function (full, start, end) {
                return start + 'ke-' + end;
            });
            return html;
        });
        KE.util.addContextmenuEvent(id);
        KE.util.addNewlineEvent(id);
        KE.util.addTabEvent(id);
        function setSelectionHandler() {
            KE.util.setSelection(id);
        }
        KE.event.input(iframeDoc, setSelectionHandler, id);
        KE.event.add(iframeDoc, 'mouseup', setSelectionHandler, id);
        KE.event.add(document, 'mousedown', setSelectionHandler, id);
        KE.onchange(id, function (id) {
            if (KE.g[id].autoSetDataMode || KE.g[id].syncType == 'auto') {
                KE.util.setData(id);
                if (KE.g[id].afterSetData) KE.g[id].afterSetData(id);
            }
            if (KE.g[id].afterChange) KE.g[id].afterChange(id);
            KE.history.add(id, KE.g[id].minChangeSize);
        });
        if (KE.browser.IE) {
            iframeDoc.body.disabled = true;
            KE.readonly(id, false);
            iframeDoc.body.removeAttribute('disabled');
        }
        KE.util.setFullHtml(id, srcTextarea.value);
        KE.history.add(id, 0);
        if (mode > 0) KE.util.focus(id);
        if (KE.g[id].afterCreate) KE.g[id].afterCreate(id);
    };

    KE.onchange = function (id, func) {
        var g = KE.g[id];
        function handler() {
            func(id);
        };
        g.onchangeHandlerStack.push(handler);
        KE.event.input(g.iframeDoc, handler, id);
        KE.event.input(g.newTextarea, handler, id);
        KE.event.add(g.iframeDoc, 'mouseup', function (e) {
            window.setTimeout(function () {
                func(id);
            }, 0);
        }, id);
    };

    var _needStyle = true;

    KE.init = function (args) {
        var g = KE.g[args.id] = args;
        g.config = {};
        g.undoStack = [];
        g.redoStack = [];
        g.dialogStack = [];
        g.contextmenuItems = [];
        g.getHtmlHooks = [];
        g.setHtmlHooks = [];
        g.onchangeHandlerStack = [];
        g.eventStack = [];
        KE.each(KE.setting, function (key, val) {
            g[key] = (typeof args[key] == 'undefined') ? val : args[key];
            g.config[key] = g[key];
        });
        if (g.loadStyleMode && _needStyle) {
            KE.util.loadStyle(g.skinsPath + g.skinType + '.css');
            _needStyle = false;
        }
    }

    KE.show = function (args) {
        KE.init(args);
        KE.event.ready(function ()
        { KE.create(args.id); }
    );
    };

    if (window.KE === undefined) window.KE = KE;
    window.KindEditor = KE;

})();

(function (KE, undefined) {

    KE.langType = 'zh_CN';

    KE.lang = {
        source: 'HTML代码',
        undo: '后退(Ctrl+Z)',
        redo: '前进(Ctrl+Y)',
        cut: '剪切(Ctrl+X)',
        copy: '复制(Ctrl+C)',
        paste: '粘贴(Ctrl+V)',
        plainpaste: '粘贴为无格式文本',
        wordpaste: '从Word粘贴',
        selectall: '全选',
        justifyleft: '左对齐',
        justifycenter: '居中',
        justifyright: '右对齐',
        justifyfull: '两端对齐',
        insertorderedlist: '编号',
        insertunorderedlist: '项目符号',
        indent: '增加缩进',
        outdent: '减少缩进',
        subscript: '下标',
        superscript: '上标',
        title: '标题',
        fontname: '字体',
        fontsize: '文字大小',
        textcolor: '文字颜色',
        bgcolor: '文字背景',
        bold: '粗体(Ctrl+B)',
        italic: '斜体(Ctrl+I)',
        underline: '下划线(Ctrl+U)',
        strikethrough: '删除线',
        removeformat: '删除格式',
        image: '图片',
        flash: '插入Flash',
        media: '插入多媒体',
        table: '插入表格',
        hr: '插入横线',
        emoticons: '插入表情',
        link: '超级链接',
        unlink: '取消超级链接',
        fullscreen: '全屏显示',
        about: '关于',
        print: '打印',
        fileManager: '浏览服务器',
        advtable: '表格',
        tablecell: '单元格',
        yes: '确定',
        no: '取消',
        close: '关闭',
        editImage: '图片属性',
        deleteImage: '删除图片',
        editLink: '超级链接属性',
        deleteLink: '取消超级链接',
        tableprop: '表格属性',
        tablecellprop: '单元格属性',
        tableinsert: '插入表格',
        tabledelete: '删除表格',
        tablecolinsertleft: '左侧插入列',
        tablecolinsertright: '右侧插入列',
        tablerowinsertabove: '上方插入行',
        tablerowinsertbelow: '下方插入行',
        tablecoldelete: '删除列',
        tablerowdelete: '删除行',
        noColor: '无颜色',
        invalidImg: "请输入有效的URL地址。\n只允许jpg,gif,bmp,png格式。",
        invalidMedia: "请输入有效的URL地址。\n只允许swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb格式。",
        invalidWidth: "宽度必须为数字。",
        invalidHeight: "高度必须为数字。",
        invalidBorder: "边框必须为数字。",
        invalidUrl: "请输入有效的URL地址。",
        invalidRows: '行数为必选项，只允许输入大于0的数字。',
        invalidCols: '列数为必选项，只允许输入大于0的数字。',
        invalidPadding: '边距必须为数字。',
        invalidSpacing: '间距必须为数字。',
        invalidBorder: '边框必须为数字。',
        pleaseInput: "请输入内容。",
        invalidJson: '服务器发生故障。',
        cutError: '您的浏览器安全设置不允许使用剪切操作，请使用快捷键(Ctrl+X)来完成。',
        copyError: '您的浏览器安全设置不允许使用复制操作，请使用快捷键(Ctrl+C)来完成。',
        pasteError: '您的浏览器安全设置不允许使用粘贴操作，请使用快捷键(Ctrl+V)来完成。'
    };

    var plugins = KE.lang.plugins = {};

    plugins.about = {
        version: KE.version,
        title: 'HTML可视化编辑器'
    };

    plugins.plainpaste = {
        comment: '请使用快捷键(Ctrl+V)把内容粘贴到下面的方框里。'
    };

    plugins.wordpaste = {
        comment: '请使用快捷键(Ctrl+V)把内容粘贴到下面的方框里。'
    };

    plugins.link = {
        url: 'URL地址',
        linkType: '打开类型',
        newWindow: '新窗口',
        selfWindow: '当前窗口'
    };

    plugins.flash = {
        url: 'Flash地址',
        width: '宽度',
        height: '高度'
    };

    plugins.media = {
        url: '媒体文件地址',
        width: '宽度',
        height: '高度',
        autostart: '自动播放'
    };

    plugins.image = {
        remoteImage: '远程图片',
        localImage: '本地上传',
        remoteUrl: '图片地址',
        localUrl: '图片地址',
        size: '图片大小',
        width: '宽',
        height: '高',
        resetSize: '重置大小',
        align: '对齐方式',
        defaultAlign: '默认方式',
        leftAlign: '左对齐',
        rightAlign: '右对齐',
        imgTitle: '图片说明',
        viewServer: '浏览...'
    };

    plugins.file_manager = {
        emptyFolder: '空文件夹',
        moveup: '移到上一级文件夹',
        viewType: '显示方式：',
        viewImage: '缩略图',
        listImage: '详细信息',
        orderType: '排序方式：',
        fileName: '名称',
        fileSize: '大小',
        fileType: '类型'
    };

    plugins.advtable = {
        cells: '单元格数',
        rows: '行数',
        cols: '列数',
        size: '大小',
        width: '宽度',
        height: '高度',
        percent: '%',
        px: 'px',
        space: '边距间距',
        padding: '边距',
        spacing: '间距',
        align: '对齐方式',
        textAlign: '水平对齐',
        verticalAlign: '垂直对齐',
        alignDefault: '默认',
        alignLeft: '左对齐',
        alignCenter: '居中',
        alignRight: '右对齐',
        alignTop: '顶部',
        alignMiddle: '中部',
        alignBottom: '底部',
        alignBaseline: '基线',
        border: '边框',
        borderWidth: '边框',
        borderColor: '颜色',
        backgroundColor: '背景颜色'
    };

    plugins.title = {
        h1: '标题 1',
        h2: '标题 2',
        h3: '标题 3',
        h4: '标题 4',
        p: '正 文'
    };

    plugins.fontname = {
        fontName: {
            'SimSun': '宋体',
            'NSimSun': '新宋体',
            'FangSong_GB2312': '仿宋_GB2312',
            'KaiTi_GB2312': '楷体_GB2312',
            'SimHei': '黑体',
            'Microsoft YaHei': '微软雅黑',
            'Arial': 'Arial',
            'Arial Black': 'Arial Black',
            'Times New Roman': 'Times New Roman',
            'Courier New': 'Courier New',
            'Tahoma': 'Tahoma',
            'Verdana': 'Verdana'
        }
    };

})(KindEditor);

(function (KE, undefined) {

    KE.plugin.about = {
        click: function (id) {
            KE.util.selection(id);
            var dialog = new KE.dialog({
                id: id,
                cmd: 'about',
                file: 'about.html',
                width: 300,
                height: 70,
                loadingMode: true,
                title: KE.lang['about'],
                noButton: KE.lang['close']
            });
            dialog.show();
        }
    };

    KE.plugin.undo = {
        init: function (id) {
            KE.event.ctrl(KE.g[id].iframeDoc, 'Z', function (e) {
                KE.plugin['undo'].click(id);
                KE.util.focus(id);
            }, id);
            KE.event.ctrl(KE.g[id].newTextarea, 'Z', function (e) {
                KE.plugin['undo'].click(id);
                KE.util.focus(id);
            }, id);
        },
        click: function (id) {
            KE.history.undo(id);
            KE.util.execOnchangeHandler(id);
        }
    };

    KE.plugin.redo = {
        init: function (id) {
            KE.event.ctrl(KE.g[id].iframeDoc, 'Y', function (e) {
                KE.plugin['redo'].click(id);
                KE.util.focus(id);
            }, id);
            KE.event.ctrl(KE.g[id].newTextarea, 'Y', function (e) {
                KE.plugin['redo'].click(id);
                KE.util.focus(id);
            }, id);
        },
        click: function (id) {
            KE.history.redo(id);
            KE.util.execOnchangeHandler(id);
        }
    };

    KE.plugin.cut = {
        click: function (id) {
            try {
                if (!KE.g[id].iframeDoc.queryCommandSupported('cut')) throw 'e';
            } catch (e) {
                alert(KE.lang.cutError);
                return;
            }
            KE.util.execCommand(id, 'cut', null);
        }
    };

    KE.plugin.copy = {
        click: function (id) {
            try {
                if (!KE.g[id].iframeDoc.queryCommandSupported('copy')) throw 'e';
            } catch (e) {
                alert(KE.lang.copyError);
                return;
            }
            KE.util.execCommand(id, 'copy', null);
        }
    };

    KE.plugin.paste = {
        click: function (id) {
            try {
                if (!KE.g[id].iframeDoc.queryCommandSupported('paste')) throw 'e';
            } catch (e) {
                alert(KE.lang.pasteError);
                return;
            }
            KE.util.execCommand(id, 'paste', null);
        }
    };

    KE.plugin.plainpaste = {
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'plainpaste',
                file: 'plainpaste.html',
                width: 450,
                height: 300,
                loadingMode: true,
                title: KE.lang['plainpaste'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        exec: function (id) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var html = KE.$('textArea', dialogDoc).value;
            html = KE.util.escape(html);
            html = html.replace(/ /g, '&nbsp;');
            if (KE.g[id].newlineTag == 'p') {
                html = html.replace(/^/, '<p>').replace(/$/, '</p>').replace(/\r\n|\n|\r/g, '</p><p>');
            } else {
                html = html.replace(/\r\n|\n|\r/g, '<br />$&');
            }
            KE.util.insertHtml(id, html);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.wordpaste = {
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'wordpaste',
                file: 'wordpaste.html',
                width: 450,
                height: 300,
                loadingMode: true,
                title: KE.lang['wordpaste'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        exec: function (id) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var wordIframe = KE.$('wordIframe', dialogDoc);
            var str = KE.util.getIframeDoc(wordIframe).body.innerHTML;
            str = str.replace(/<meta(\n|.)*?>/ig, "");
            str = str.replace(/<!(\n|.)*?>/ig, "");
            str = str.replace(/<style[^>]*>(\n|.)*?<\/style>/ig, "");
            str = str.replace(/<script[^>]*>(\n|.)*?<\/script>/ig, "");
            str = str.replace(/<w:[^>]+>(\n|.)*?<\/w:[^>]+>/ig, "");
            str = str.replace(/<xml>(\n|.)*?<\/xml>/ig, "");
            str = str.replace(/\r\n|\n|\r/ig, "");
            str = KE.util.execGetHtmlHooks(id, str);
            str = KE.format.getHtml(str, KE.g[id].htmlTags, KE.g[id].urlType);
            KE.util.insertHtml(id, str);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.fullscreen = {
        click: function (id) {
            var g = KE.g[id];
            var self = this;
            var resetSize = function () {
                var el = KE.util.getDocumentElement();
                g.width = el.clientWidth + 'px';
                g.height = el.clientHeight + 'px';
            };
            var windowSize = '';
            var resizeListener = function () {
                if (!self.isSelected) return;
                var el = KE.util.getDocumentElement();
                var size = [el.clientWidth, el.clientHeight].join('');
                if (windowSize != size) {
                    windowSize = size;
                    resetSize();
                    KE.util.resize(id, g.width, g.height);
                }
            }
            if (this.isSelected) {
                this.isSelected = false;
                KE.util.setData(id);
                KE.remove(id, 1);
                g.width = this.width;
                g.height = this.height;
                KE.create(id, 2);
                document.body.parentNode.style.overflow = 'auto';
                KE.event.remove(window, 'resize', resizeListener);
                g.resizeMode = g.config.resizeMode;
                KE.toolbar.unselect(id, "fullscreen");
            } else {
                this.isSelected = true;
                this.width = g.container.style.width;
                this.height = g.container.style.height;
                KE.util.setData(id);
                KE.remove(id, 2);
                document.body.parentNode.style.overflow = 'hidden';
                resetSize();
                KE.create(id, 1);
                var pos = KE.util.getScrollPos();
                var div = g.container;
                div.style.position = 'absolute';
                div.style.left = pos.x + 'px';
                div.style.top = pos.y + 'px';
                div.style.zIndex = 19811211;
                KE.event.add(window, 'resize', resizeListener);
                g.resizeMode = 0;
                KE.toolbar.select(id, "fullscreen");
            }
        }
    };

    KE.plugin.bgcolor = {
        click: function (id) {
            KE.util.selection(id);
            var color = KE.queryCommandValue(KE.g[id].iframeDoc, 'bgcolor');
            this.menu = new KE.menu({
                id: id,
                cmd: 'bgcolor'
            });
            this.menu.picker(color);
        },
        exec: function (id, value) {
            var cmd = new KE.cmd(id);
            if (value == '') {
                cmd.remove({
                    'span': ['.background-color']
                });
            } else {
                cmd.wrap('span', [{ '.background-color': value}]);
            }
            KE.util.execOnchangeHandler(id);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.fontname = {
        click: function (id) {
            var fontName = KE.lang.plugins.fontname.fontName;
            var cmd = 'fontname';
            KE.util.selection(id);
            var menu = new KE.menu({
                id: id,
                cmd: cmd,
                width: 150
            });
            var font = KE.queryCommandValue(KE.g[id].iframeDoc, cmd);
            KE.each(fontName, function (key, value) {
                var html = '<span class="ke-reset" style="font-family: ' + key + ';">' + value + '</span>';
                menu.add(
				html,
				function () { KE.plugin[cmd].exec(id, key); },
				{ checked: (font === key.toLowerCase() || font === value.toLowerCase()) }
			);
            });
            menu.show();
            this.menu = menu;
        },
        exec: function (id, value) {
            var cmd = new KE.cmd(id);
            cmd.wrap('span', [{ '.font-family': value}]);
            KE.util.execOnchangeHandler(id);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.fontsize = {
        click: function (id) {
            var fontSize = ['9px', '10px', '12px', '14px', '16px', '18px', '24px', '32px'];
            var cmd = 'fontsize';
            KE.util.selection(id);
            var size = KE.queryCommandValue(KE.g[id].iframeDoc, 'fontsize');
            var menu = new KE.menu({
                id: id,
                cmd: cmd,
                width: 120
            });
            for (var i = 0, len = fontSize.length; i < len; i++) {
                var value = fontSize[i];
                var html = '<span class="ke-reset" style="font-size: ' + value + ';">' + value + '</span>';
                menu.add(
				html,
				(function (value) {
				    return function () {
				        KE.plugin[cmd].exec(id, value);
				    };
				})(value),
				{
				    height: (parseInt(value) + 12) + 'px',
				    checked: (size === value)
				}
			);
            }
            menu.show();
            this.menu = menu;
        },
        exec: function (id, value) {
            var cmd = new KE.cmd(id);
            cmd.wrap('span', [{ '.font-size': value}]);
            KE.util.execOnchangeHandler(id);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.hr = {
        click: function (id) {
            KE.util.selection(id);
            KE.util.insertHtml(id, '<hr />');
            KE.util.focus(id);
        }
    };

    KE.plugin.print = {
        click: function (id) {
            KE.util.selection(id);
            KE.g[id].iframeWin.print();
        }
    };

    KE.plugin.removeformat = {
        click: function (id) {
            KE.util.selection(id);
            var cmd = new KE.cmd(id);
            var tags = {
                '*': ['class', 'style']
            };
            for (var i = 0, len = KE.g[id].inlineTags.length; i < len; i++) {
                tags[KE.g[id].inlineTags[i]] = ['*'];
            }
            cmd.remove(tags);
            KE.util.execOnchangeHandler(id);
            KE.toolbar.updateState(id);
            KE.util.focus(id);
        }
    };

    KE.plugin.source = {
        click: function (id) {
            var g = KE.g[id];
            if (!g.wyswygMode) {
                KE.util.setFullHtml(id, g.newTextarea.value);
                g.iframe.style.display = 'block';
                g.newTextarea.style.display = 'none';
                KE.toolbar.able(id, ['source', 'fullscreen']);
                g.wyswygMode = true;
                this.isSelected = false;
                KE.toolbar.unselect(id, "source");
            } else {
                KE.hideMenu(id);
                g.newTextarea.value = KE.util.getData(id);
                g.iframe.style.display = 'none';
                g.newTextarea.style.display = 'block';
                KE.toolbar.disable(id, ['source', 'fullscreen']);
                g.wyswygMode = false;
                this.isSelected = true;
                KE.toolbar.select(id, "source");
            }
            KE.util.focus(id);
        }
    };

    KE.plugin.textcolor = {
        click: function (id) {
            KE.util.selection(id);
            var color = KE.queryCommandValue(KE.g[id].iframeDoc, 'textcolor');
            this.menu = new KE.menu({
                id: id,
                cmd: 'textcolor'
            });
            this.menu.picker(color);
        },
        exec: function (id, value) {
            var cmd = new KE.cmd(id);
            if (value == '') {
                cmd.remove({
                    'span': ['.color'],
                    'font': ['color']
                });
            } else {
                cmd.wrap('span', [{ '.color': value}]);
            }
            KE.util.execOnchangeHandler(id);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.title = {
        click: function (id) {
            var lang = KE.lang.plugins.title;
            var title = {
                'H1': lang.h1,
                'H2': lang.h2,
                'H3': lang.h3,
                'H4': lang.h4,
                'P': lang.p
            };
            var sizeHash = {
                'H1': 28,
                'H2': 24,
                'H3': 18,
                'H4': 14,
                'P': 12
            };
            var cmd = 'title';
            KE.util.selection(id);
            var block = KE.queryCommandValue(KE.g[id].iframeDoc, 'formatblock');
            var menu = new KE.menu({
                id: id,
                cmd: cmd,
                width: (KE.langType == 'en' ? 200 : 150)
            });
            KE.each(title, function (key, value) {
                var style = 'font-size:' + sizeHash[key] + 'px;'
                if (key !== 'P') style += 'font-weight:bold;';
                var html = '<span class="ke-reset" style="' + style + '">' + value + '</span>';
                menu.add(html, function () {
                    KE.plugin[cmd].exec(id, '<' + key + '>');
                },
				{
				    height: (sizeHash[key] + 12) + 'px',
				    checked: (block === key.toLowerCase() || block === value.toLowerCase())
				}
			);
            });
            menu.show();
            this.menu = menu;
        },
        exec: function (id, value) {
            KE.util.select(id);
            KE.util.execCommand(id, 'formatblock', value);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.emoticons = {
        click: function (id) {
            var self = this,
			cmd = 'emoticons',
			rows = 5,
			cols = 9,
			total = 135,
			startNum = 0,
			cells = rows * cols,
			pages = Math.ceil(total / cells),
			colsHalf = Math.floor(cols / 2),
			g = KE.g[id],
			path = g.pluginsPath + 'emoticons/',
			allowPreview = (g.allowPreviewEmoticons === undefined) ? true : g.allowPreviewEmoticons;
            KE.util.selection(id);
            var wrapperDiv = KE.$$('div');
            wrapperDiv.className = 'ke-plugin-emoticons-wrapper';
            var previewDiv, previewImg;
            if (allowPreview) {
                previewDiv = KE.$$('div');
                previewDiv.className = 'ke-plugin-emoticons-preview';
                previewDiv.style.right = 0;
                var previewImg = KE.$$('img');
                previewImg.className = 'ke-reset';
                previewImg.src = path + '0.gif';
                previewImg.border = 0;
                previewDiv.appendChild(previewImg);
                wrapperDiv.appendChild(previewDiv);
            }
            function createEmoticonsTable(pageNum) {
                var table = KE.$$('table');
                if (previewDiv) {
                    table.onmouseover = function () { previewDiv.style.display = 'block'; };
                    table.onmouseout = function () { previewDiv.style.display = 'none'; };
                }
                table.className = 'ke-plugin-emoticons-table';
                table.cellPadding = 0;
                table.cellSpacing = 0;
                table.border = 0;
                var num = (pageNum - 1) * cells + startNum;
                for (var i = 0; i < rows; i++) {
                    var row = table.insertRow(i);
                    for (var j = 0; j < cols; j++) {
                        var cell = row.insertCell(j);
                        cell.className = 'ke-plugin-emoticons-cell';
                        if (previewDiv) {
                            cell.onmouseover = (function (j, num) {
                                return function () {
                                    if (j > colsHalf) {
                                        previewDiv.style.left = 0;
                                        previewDiv.style.right = '';
                                    } else {
                                        previewDiv.style.left = '';
                                        previewDiv.style.right = 0;
                                    }
                                    previewImg.src = path + num + '.gif'; ;
                                    this.className = 'ke-plugin-emoticons-cell ke-plugin-emoticons-cell-on';
                                };
                            })(j, num);
                        } else {
                            cell.onmouseover = function () {
                                this.className = 'ke-plugin-emoticons-cell ke-plugin-emoticons-cell-on';
                            };
                        }
                        cell.onmouseout = function () { this.className = 'ke-plugin-emoticons-cell'; };
                        cell.onclick = (function (num) {
                            return function () {
                                self.exec(id, num);
                                return false;
                            };
                        })(num);
                        var span = KE.$$('span');
                        span.className = 'ke-plugin-emoticons-img';
                        span.style.backgroundPosition = '-' + (24 * num) + 'px 0px';
                        cell.appendChild(span);
                        num++;
                    }
                }
                return table;
            }
            var table = createEmoticonsTable(1);
            wrapperDiv.appendChild(table);
            var pageDiv = KE.$$('div');
            pageDiv.className = 'ke-plugin-emoticons-page';
            wrapperDiv.appendChild(pageDiv);
            function createPageTable(currentPageNum) {
                for (var pageNum = 1; pageNum <= pages; pageNum++) {
                    if (currentPageNum !== pageNum) {
                        var a = KE.$$('a');
                        a.href = 'javascript:;';
                        a.innerHTML = '[' + pageNum + ']';
                        a.onclick = (function (pageNum) {
                            return function () {
                                wrapperDiv.removeChild(table);
                                var newTable = createEmoticonsTable(pageNum);
                                wrapperDiv.insertBefore(newTable, pageDiv);
                                table = newTable;
                                pageDiv.innerHTML = '';
                                createPageTable(pageNum);
                                return false;
                            };
                        })(pageNum);
                        pageDiv.appendChild(a);
                    } else {
                        pageDiv.appendChild(document.createTextNode('[' + pageNum + ']'));
                    }
                    pageDiv.appendChild(document.createTextNode(' '));
                }
            }
            createPageTable(1);
            var menu = new KE.menu({
                id: id,
                cmd: cmd
            });
            menu.append(wrapperDiv);
            menu.show();
            this.menu = menu;
        },
        exec: function (id, num) {
            var src = KE.g[id].pluginsPath + 'emoticons/' + num + '.gif';
            var html = '<img src="' + src + '" kesrc="' + src + '" alt="" />';
            KE.util.insertHtml(id, html);
            this.menu.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.flash = {
        init: function (id) {
            var self = this;
            KE.g[id].getHtmlHooks.push(function (html) {
                return html.replace(/<img[^>]*class="?ke-flash"?[^>]*>/ig, function (imgStr) {
                    var width = imgStr.match(/style="[^"]*;?\s*width:\s*(\d+)/i) ? RegExp.$1 : 0;
                    var height = imgStr.match(/style="[^"]*;?\s*height:\s*(\d+)/i) ? RegExp.$1 : 0;
                    width = width || (imgStr.match(/width="([^"]+)"/i) ? RegExp.$1 : 0);
                    height = height || (imgStr.match(/height="([^"]+)"/i) ? RegExp.$1 : 0);
                    if (imgStr.match(/kesrctag="([^"]+)"/i)) {
                        var attrs = KE.util.getAttrList(unescape(RegExp.$1));
                        attrs.width = width || attrs.width || 0;
                        attrs.height = height || attrs.height || 0;
                        attrs.kesrc = attrs.src;
                        return KE.util.getMediaEmbed(attrs);
                    }
                });
            });
            KE.g[id].setHtmlHooks.push(function (html) {
                return html.replace(/<embed[^>]*type="application\/x-shockwave-flash"[^>]*>(?:<\/embed>)?/ig, function ($0) {
                    var src = $0.match(/\s+src="([^"]+)"/i) ? RegExp.$1 : '';
                    if ($0.match(/\s+kesrc="([^"]+)"/i)) src = RegExp.$1;
                    var width = $0.match(/\s+width="([^"]+)"/i) ? RegExp.$1 : 0;
                    var height = $0.match(/\s+height="([^"]+)"/i) ? RegExp.$1 : 0;
                    var attrs = KE.util.getAttrList($0);
                    attrs.src = src;
                    attrs.width = width;
                    attrs.height = height;
                    return KE.util.getMediaImage(id, 'flash', attrs);
                });
            });
        },
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'flash',
                file: 'flash.html',
                width: 400,
                height: 140,
                loadingMode: true,
                title: KE.lang['flash'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        check: function (id, url, width, height) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            if (!url.match(/^.{3,}$/)) {
                alert(KE.lang['invalidUrl']);
                KE.$('url', dialogDoc).focus();
                return false;
            }
            if (!width.match(/^\d*$/)) {
                alert(KE.lang['invalidWidth']);
                KE.$('width', dialogDoc).focus();
                return false;
            }
            if (!height.match(/^\d*$/)) {
                alert(KE.lang['invalidHeight']);
                KE.$('height', dialogDoc).focus();
                return false;
            }
            return true;
        },
        exec: function (id) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var url = KE.$('url', dialogDoc).value;
            var width = KE.$('width', dialogDoc).value;
            var height = KE.$('height', dialogDoc).value;
            if (!this.check(id, url, width, height)) return false;
            var html = KE.util.getMediaImage(id, 'flash', {
                src: url,
                type: KE.g[id].mediaTypes['flash'],
                width: width,
                height: height,
                quality: 'high'
            });
            KE.util.insertHtml(id, html);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.image = {
        getSelectedNode: function (id) {
            var g = KE.g[id];
            var startNode = g.keRange.startNode;
            var endNode = g.keRange.endNode;
            if (!KE.browser.WEBKIT && !g.keSel.isControl) return;
            if (startNode.nodeType != 1) return;
            if (startNode.tagName.toLowerCase() != 'img') return;
            if (startNode != endNode) return;
            if (!startNode.className.match(/^ke-\w+/i)) return startNode;
        },
        init: function (id) {
            var self = this;
            var g = KE.g[id];
            g.contextmenuItems.push({
                text: KE.lang['editImage'],
                click: function (id, menu) {
                    KE.util.select(id);
                    menu.hide();
                    self.click(id);
                },
                cond: function (id) {
                    return self.getSelectedNode(id);
                },
                options: {
                    width: '150px',
                    iconHtml: '<span class="ke-common-icon ke-common-icon-url ke-icon-image"></span>'
                }
            });
            g.contextmenuItems.push({
                text: KE.lang['deleteImage'],
                click: function (id, menu) {
                    KE.util.select(id);
                    menu.hide();
                    var img = self.getSelectedNode(id);
                    img.parentNode.removeChild(img);
                    KE.util.execOnchangeHandler(id);
                },
                cond: function (id) {
                    return self.getSelectedNode(id);
                },
                options: {
                    width: '150px'
                }
            });
            g.contextmenuItems.push('-');
        },
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'image',
                file: 'image/image.html',
                width: 400,
                height: 220,
                loadingMode: true,
                title: KE.lang['image'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        check: function (id) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var type = KE.$('type', dialogDoc).value;
            var width = KE.$('imgWidth', dialogDoc).value;
            var height = KE.$('imgHeight', dialogDoc).value;
            var title = KE.$('imgTitle', dialogDoc).value;
            var urlBox;
            if (type == 2) {
                urlBox = KE.$('imgFile', dialogDoc);
            } else {
                urlBox = KE.$('url', dialogDoc);
            }
            if (!urlBox.value.match(/\.(jpg|jpeg|gif|bmp|png)(\s|\?|$)/i)) {
                alert(KE.lang['invalidImg']);
                urlBox.focus();
                return false;
            }
            if (!width.match(/^\d*$/)) {
                alert(KE.lang['invalidWidth']);
                KE.$('imgWidth', dialogDoc).focus();
                return false;
            }
            if (!height.match(/^\d*$/)) {
                alert(KE.lang['invalidHeight']);
                KE.$('imgHeight', dialogDoc).focus();
                return false;
            }
            return true;
        },
        exec: function (id) {
            var self = this;
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var type = KE.$('type', dialogDoc).value;
            var width = KE.$('imgWidth', dialogDoc).value;
            var height = KE.$('imgHeight', dialogDoc).value;
            var title = KE.$('imgTitle', dialogDoc).value;
            var alignElements = dialogDoc.getElementsByName('align');
            var align = '';
            for (var i = 0, len = alignElements.length; i < len; i++) {
                if (alignElements[i].checked) {
                    align = alignElements[i].value;
                    break;
                }
            }
            if (!this.check(id)) return false;
            if (type == 2) {
                KE.$('editorId', dialogDoc).value = id;
                var uploadIframe = KE.$('uploadIframe', dialogDoc);
                KE.util.showLoadingPage(id);
                var onloadFunc = function () {
                    KE.event.remove(uploadIframe, 'load', onloadFunc);
                    KE.util.hideLoadingPage(id);
                    var uploadDoc = KE.util.getIframeDoc(uploadIframe);
                    var data = '';
                    try {
                        data = KE.util.parseJson(uploadDoc.body.innerHTML);
                    } catch (e) {
                        alert(KE.lang.invalidJson);
                    }
                    if (typeof data === 'object' && 'error' in data) {
                        if (data.error === 0) {
                            var url = KE.format.getUrl(data.url, 'absolute');
                            self.insert(id, url, title, width, height, 0, align);
                        } else {
                            alert(data.message);
                            return false;
                        }
                    }
                };
                KE.event.add(uploadIframe, 'load', onloadFunc);
                dialogDoc.uploadForm.submit();
                return;
            } else {
                var url = KE.$('url', dialogDoc).value;
                this.insert(id, url, title, width, height, 0, align);
            }
        },
        insert: function (id, url, title, width, height, border, align) {
            var html = '<img src="' + url + '" kesrc="' + url + '" ';
            if (width > 0) html += 'width="' + width + '" ';
            if (height > 0) html += 'height="' + height + '" ';
            if (title) html += 'title="' + title + '" ';
            if (align) html += 'align="' + align + '" ';
            html += 'alt="' + title + '" ';
            if (border) html += 'border="' + border + '" ';
            html += '/>';
            KE.util.insertHtml(id, html);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.link = {
        getSelectedNode: function (id) {
            return KE.getCommonAncestor(KE.g[id].keSel, 'a');
        },
        init: function (id) {
            var self = this;
            KE.g[id].contextmenuItems.push({
                text: KE.lang['editLink'],
                click: function (id, menu) {
                    KE.util.select(id);
                    menu.hide();
                    self.click(id);
                },
                cond: function (id) {
                    return self.getSelectedNode(id);
                },
                options: {
                    width: '150px',
                    iconHtml: '<span class="ke-common-icon ke-common-icon-url ke-icon-link"></span>'
                }
            });
        },
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'link',
                file: 'link/link.html',
                width: 400,
                height: 90,
                loadingMode: true,
                title: KE.lang['link'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        exec: function (id) {
            var g = KE.g[id];
            KE.util.select(id);
            var range = g.keRange;
            var startNode = range.startNode;
            var endNode = range.endNode;
            var iframeDoc = g.iframeDoc;
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var url = KE.$('hyperLink', dialogDoc).value;
            var target = KE.$('linkType', dialogDoc).value;
            if (!url.match(/.+/) || url.match(/^\w+:\/\/\/?$/)) {
                alert(KE.lang['invalidUrl']);
                KE.$('hyperLink', dialogDoc).focus();
                return false;
            }
            var node = range.getParentElement();
            while (node) {
                if (node.tagName.toLowerCase() == 'a' || node.tagName.toLowerCase() == 'body') break;
                node = node.parentNode;
            }
            node = node.parentNode;
            var isItem;
            if (KE.browser.IE) {
                isItem = !!g.range.item;
            } else {
                isItem = (startNode.nodeType == 1 && startNode === endNode && startNode.nodeName.toLowerCase() != 'br');
            }
            var isEmpty = !isItem;
            if (!isItem) isEmpty = KE.browser.IE ? g.range.text === '' : g.range.toString() === '';
            if (isEmpty || KE.util.isEmpty(id)) {
                var html = '<a href="' + url + '"';
                if (target) html += ' target="' + target + '"';
                html += '>' + url + '</a>';
                KE.util.insertHtml(id, html);
            } else {
                iframeDoc.execCommand('createlink', false, '__ke_temp_url__');
                var arr = node.getElementsByTagName('a');
                for (var i = 0, l = arr.length; i < l; i++) {
                    if (arr[i].href.match(/\/?__ke_temp_url__$/)) {
                        arr[i].href = url;
                        arr[i].setAttribute('kesrc', url);
                        if (target) arr[i].target = target;
                        else arr[i].removeAttribute('target');
                    }
                }
                if (KE.browser.WEBKIT && isItem && startNode.tagName.toLowerCase() == 'img') {
                    var parent = startNode.parentNode;
                    if (parent.tagName.toLowerCase() != 'a') {
                        var a = KE.$$('a', iframeDoc);
                        parent.insertBefore(a, startNode);
                        a.appendChild(startNode);
                        parent = a;
                    }
                    parent.href = url;
                    parent.setAttribute('kesrc', url);
                    if (target) parent.target = target;
                    else parent.removeAttribute('target');
                    g.keSel.addRange(range);
                }
            }
            KE.util.execOnchangeHandler(id);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.unlink = {
        init: function (id) {
            var self = this;
            KE.g[id].contextmenuItems.push({
                text: KE.lang['deleteLink'],
                click: function (id, menu) {
                    KE.util.select(id);
                    menu.hide();
                    self.click(id);
                },
                cond: function (id) {
                    return KE.plugin['link'].getSelectedNode(id);
                },
                options: {
                    width: '150px',
                    iconHtml: '<span class="ke-common-icon ke-common-icon-url ke-icon-unlink"></span>'
                }
            });
            KE.g[id].contextmenuItems.push('-');
        },
        click: function (id) {
            var g = KE.g[id];
            var iframeDoc = g.iframeDoc;
            KE.util.selection(id);
            var range = g.keRange;
            var startNode = range.startNode;
            var endNode = range.endNode;
            var isItem = (startNode.nodeType == 1 && startNode === endNode);
            var isEmpty = !isItem;
            if (!isItem) isEmpty = KE.browser.IE ? g.range.text === '' : g.range.toString() === '';
            if (isEmpty) {
                var linkNode = KE.plugin['link'].getSelectedNode(id);
                if (!linkNode) return;
                var range = g.keRange;
                range.selectTextNode(linkNode);
                g.keSel.addRange(range);
                KE.util.select(id);
                iframeDoc.execCommand('unlink', false, null);
                if (KE.browser.WEBKIT && startNode.tagName.toLowerCase() == 'img') {
                    var parent = startNode.parentNode;
                    if (parent.tagName.toLowerCase() == 'a') {
                        KE.util.removeParent(parent);
                        g.keSel.addRange(range);
                    }
                }
            } else {
                iframeDoc.execCommand('unlink', false, null);
            }
            KE.util.execOnchangeHandler(id);
            KE.toolbar.updateState(id);
            KE.util.focus(id);
        }
    };

    KE.plugin.media = {
        init: function (id) {
            var self = this;
            var typeHash = {};
            KE.each(KE.g[id].mediaTypes, function (key, val) {
                typeHash[val] = key;
            });
            KE.g[id].getHtmlHooks.push(function (html) {
                return html.replace(/<img[^>]*class="?ke-\w+"?[^>]*>/ig, function ($0) {
                    var width = $0.match(/style="[^"]*;?\s*width:\s*(\d+)/i) ? RegExp.$1 : 0;
                    var height = $0.match(/style="[^"]*;?\s*height:\s*(\d+)/i) ? RegExp.$1 : 0;
                    width = width || ($0.match(/width="([^"]+)"/i) ? RegExp.$1 : 0);
                    height = height || ($0.match(/height="([^"]+)"/i) ? RegExp.$1 : 0);
                    if ($0.match(/\s+kesrctag="([^"]+)"/i)) {
                        var attrs = KE.util.getAttrList(unescape(RegExp.$1));
                        attrs.width = width || attrs.width || 0;
                        attrs.height = height || attrs.height || 0;
                        attrs.kesrc = attrs.src;
                        return KE.util.getMediaEmbed(attrs);
                    }
                });
            });
            KE.g[id].setHtmlHooks.push(function (html) {
                return html.replace(/<embed[^>]*type="([^"]+)"[^>]*>(?:<\/embed>)?/ig, function ($0, $1) {
                    if (typeof typeHash[$1] == 'undefined') return $0;
                    var src = $0.match(/\s+src="([^"]+)"/i) ? RegExp.$1 : '';
                    if ($0.match(/\s+kesrc="([^"]+)"/i)) src = RegExp.$1;
                    var width = $0.match(/\s+width="([^"]+)"/i) ? RegExp.$1 : 0;
                    var height = $0.match(/\s+height="([^"]+)"/i) ? RegExp.$1 : 0;
                    var attrs = KE.util.getAttrList($0);
                    attrs.src = src;
                    attrs.width = width;
                    attrs.height = height;
                    return KE.util.getMediaImage(id, '', attrs);
                });
            });
        },
        click: function (id) {
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: 'media',
                file: 'media.html',
                width: 400,
                height: 170,
                loadingMode: true,
                title: KE.lang['media'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        check: function (id, url, width, height) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            if (!url.match(/^.{3,}\.(swf|flv|mp3|wav|wma|wmv|mid|avi|mpg|mpeg|asf|rm|rmvb)(\?|$)/i)) {
                alert(KE.lang['invalidMedia']);
                KE.$('url', dialogDoc).focus();
                return false;
            }
            if (!width.match(/^\d*$/)) {
                alert(KE.lang['invalidWidth']);
                KE.$('width', dialogDoc).focus();
                return false;
            }
            if (!height.match(/^\d*$/)) {
                alert(KE.lang['invalidHeight']);
                KE.$('height', dialogDoc).focus();
                return false;
            }
            return true;
        },
        exec: function (id) {
            var dialogDoc = KE.util.getIframeDoc(this.dialog.iframe);
            var url = KE.$('url', dialogDoc).value;
            var width = KE.$('width', dialogDoc).value;
            var height = KE.$('height', dialogDoc).value;
            if (!this.check(id, url, width, height)) return false;
            var autostart = KE.$('autostart', dialogDoc).checked ? 'true' : 'false';
            var html = KE.util.getMediaImage(id, '', {
                src: url,
                type: KE.g[id].mediaTypes[KE.util.getMediaType(url)],
                width: width,
                height: height,
                autostart: autostart,
                loop: 'true'
            });
            KE.util.insertHtml(id, html);
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

    KE.plugin.advtable = {
        getSelectedTable: function (id) {
            return KE.getCommonAncestor(KE.g[id].keSel, 'table');
        },
        getSelectedRow: function (id) {
            return KE.getCommonAncestor(KE.g[id].keSel, 'tr');
        },
        getSelectedCell: function (id) {
            return KE.getCommonAncestor(KE.g[id].keSel, 'td');
        },
        tableprop: function (id) {
            this.click(id);
        },
        tablecellprop: function (id) {
            var self = this;
            KE.util.selection(id);
            var dialog = new KE.dialog({
                id: id,
                cmd: 'advtable',
                file: 'advtable/cell.html',
                width: 420,
                height: 150,
                loadingMode: true,
                title: KE.lang['tablecell'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no'],
                yesClickFn: function (id) {
                    var dialogDoc = KE.util.getIframeDoc(dialog.iframe),
					widthBox = KE.$('width', dialogDoc),
					heightBox = KE.$('height', dialogDoc),
					widthTypeBox = KE.$('widthType', dialogDoc),
					heightTypeBox = KE.$('heightType', dialogDoc),
					textAlignBox = KE.$('textAlign', dialogDoc),
					verticalAlignBox = KE.$('verticalAlign', dialogDoc),
					borderBox = KE.$('border', dialogDoc),
					borderColorBox = KE.$('borderColor', dialogDoc),
					backgroundColorBox = KE.$('backgroundColor', dialogDoc),
					width = widthBox.value,
					height = heightBox.value,
					widthType = widthTypeBox.value,
					heightType = heightTypeBox.value,
					textAlign = textAlignBox.value,
					verticalAlign = verticalAlignBox.value,
					border = borderBox.value,
					borderColor = borderColorBox.innerHTML,
					backgroundColor = backgroundColorBox.innerHTML;
                    if (!width.match(/^\d*$/)) {
                        alert(KE.lang['invalidWidth']);
                        widthBox.focus();
                        return false;
                    }
                    if (!height.match(/^\d*$/)) {
                        alert(KE.lang['invalidHeight']);
                        heightBox.focus();
                        return false;
                    }
                    if (!border.match(/^\d*$/)) {
                        alert(KE.lang['invalidBorder']);
                        borderBox.focus();
                        return false;
                    }
                    var cell = self.getSelectedCell(id);
                    cell.style.width = width !== '' ? (width + widthType) : '';
                    cell.style.height = height !== '' ? (height + heightType) : '';
                    cell.style.backgroundColor = backgroundColor;
                    cell.style.textAlign = textAlign;
                    cell.style.verticalAlign = verticalAlign;
                    cell.style.borderWidth = border;
                    cell.style.borderStyle = border !== '' ? 'solid' : '';
                    cell.style.borderColor = borderColor;
                    KE.util.execOnchangeHandler(id);
                    dialog.hide();
                    KE.util.focus(id);
                }
            });
            dialog.show();
        },
        tableinsert: function (id) {
            this.click(id, 'insert');
        },
        tabledelete: function (id) {
            var table = this.getSelectedTable(id);
            table.parentNode.removeChild(table);
        },
        tablecolinsert: function (id, offset) {
            var table = this.getSelectedTable(id),
			cell = this.getSelectedCell(id),
			index = cell.cellIndex + offset;
            for (var i = 0, len = table.rows.length; i < len; i++) {
                var newCell = table.rows[i].insertCell(index);
                newCell.innerHTML = '&nbsp;';
            }
        },
        tablecolinsertleft: function (id) {
            this.tablecolinsert(id, 0);
        },
        tablecolinsertright: function (id) {
            this.tablecolinsert(id, 1);
        },
        tablerowinsert: function (id, offset) {
            var table = this.getSelectedTable(id),
			row = this.getSelectedRow(id),
			newRow = table.insertRow(row.rowIndex + offset);
            for (var i = 0, len = row.cells.length; i < len; i++) {
                var cell = newRow.insertCell(i);
                cell.innerHTML = '&nbsp;';
            }
        },
        tablerowinsertabove: function (id) {
            this.tablerowinsert(id, 0);
        },
        tablerowinsertbelow: function (id) {
            this.tablerowinsert(id, 1);
        },
        tablecoldelete: function (id) {
            var table = this.getSelectedTable(id),
			cell = this.getSelectedCell(id),
			index = cell.cellIndex;
            for (var i = 0, len = table.rows.length; i < len; i++) {
                table.rows[i].deleteCell(index);
            }
        },
        tablerowdelete: function (id) {
            var table = this.getSelectedTable(id);
            var row = this.getSelectedRow(id);
            table.deleteRow(row.rowIndex);
        },
        init: function (id) {
            var self = this;
            var zeroborder = 'ke-zeroborder';
            var tableCmds = 'prop,cellprop,colinsertleft,colinsertright,rowinsertabove,rowinsertbelow,coldelete,rowdelete,insert,delete'.split(',');
            for (var i = 0, len = tableCmds.length; i < len; i++) {
                var name = 'table' + tableCmds[i];
                KE.g[id].contextmenuItems.push({
                    text: KE.lang[name],
                    click: (function (name) {
                        return function (id, menu) {
                            KE.util.select(id);
                            menu.hide();
                            if (self[name] !== undefined) self[name](id);
                            if (!/prop/.test(name)) {
                                KE.util.execOnchangeHandler(id);
                            }
                        };
                    })(name),
                    cond: (function (name) {
                        if (KE.util.inArray(name, ['tableprop', 'tabledelete'])) {
                            return function (id) {
                                return self.getSelectedTable(id);
                            };
                        } else {
                            return function (id) {
                                return self.getSelectedCell(id);
                            };
                        }
                    })(name),
                    options: {
                        width: '170px',
                        iconHtml: '<span class="ke-common-icon ke-common-icon-url ke-icon-' + name + '"></span>'
                    }
                });
            }
            KE.g[id].contextmenuItems.push('-');
            KE.g[id].setHtmlHooks.push(function (html) {
                return html.replace(/<table([^>]*)>/ig, function ($0, $1) {
                    if ($1.match(/\s+border=["']?(\d*)["']?/ig)) {
                        var border = RegExp.$1;
                        if ($1.indexOf(zeroborder) < 0 && (border === '' || border === '0')) {
                            return KE.addClass($0, zeroborder);
                        } else {
                            return $0;
                        }
                    } else {
                        return KE.addClass($0, zeroborder);
                    }
                });
            });
        },
        click: function (id, mode) {
            mode = mode || 'default';
            var cmd = 'advtable';
            KE.util.selection(id);
            this.dialog = new KE.dialog({
                id: id,
                cmd: cmd,
                file: 'advtable/advtable.html?mode=' + mode,
                width: 420,
                height: 220,
                loadingMode: true,
                title: KE.lang['advtable'],
                yesButton: KE.lang['yes'],
                noButton: KE.lang['no']
            });
            this.dialog.show();
        },
        exec: function (id) {
            var zeroborder = 'ke-zeroborder',
			dialogDoc = KE.util.getIframeDoc(this.dialog.iframe),
			modeBox = KE.$('mode', dialogDoc),
			rowsBox = KE.$('rows', dialogDoc),
			colsBox = KE.$('cols', dialogDoc),
			widthBox = KE.$('width', dialogDoc),
			heightBox = KE.$('height', dialogDoc),
			widthTypeBox = KE.$('widthType', dialogDoc),
			heightTypeBox = KE.$('heightType', dialogDoc),
			paddingBox = KE.$('padding', dialogDoc),
			spacingBox = KE.$('spacing', dialogDoc),
			alignBox = KE.$('align', dialogDoc),
			borderBox = KE.$('border', dialogDoc),
			borderColorBox = KE.$('borderColor', dialogDoc),
			backgroundColorBox = KE.$('backgroundColor', dialogDoc),
			rows = rowsBox.value,
			cols = colsBox.value,
			width = widthBox.value,
			height = heightBox.value,
			widthType = widthTypeBox.value,
			heightType = heightTypeBox.value,
			padding = paddingBox.value,
			spacing = spacingBox.value,
			align = alignBox.value,
			border = borderBox.value,
			borderColor = borderColorBox.innerHTML,
			backgroundColor = backgroundColorBox.innerHTML;
            if (rows == '' || rows == 0 || !rows.match(/^\d*$/)) {
                alert(KE.lang['invalidRows']);
                rowsBox.focus();
                return false;
            }
            if (cols == '' || cols == 0 || !cols.match(/^\d*$/)) {
                alert(KE.lang['invalidCols']);
                colsBox.focus();
                return false;
            }
            if (!width.match(/^\d*$/)) {
                alert(KE.lang['invalidWidth']);
                widthBox.focus();
                return false;
            }
            if (!height.match(/^\d*$/)) {
                alert(KE.lang['invalidHeight']);
                heightBox.focus();
                return false;
            }
            if (!padding.match(/^\d*$/)) {
                alert(KE.lang['invalidPadding']);
                paddingBox.focus();
                return false;
            }
            if (!spacing.match(/^\d*$/)) {
                alert(KE.lang['invalidSpacing']);
                spacingBox.focus();
                return false;
            }
            if (!border.match(/^\d*$/)) {
                alert(KE.lang['invalidBorder']);
                borderBox.focus();
                return false;
            }
            if (modeBox.value === 'update') {
                var table = this.getSelectedTable(id);
                table.style.width = width !== '' ? (width + widthType) : '';
                table.style.height = height !== '' ? (height + heightType) : '';
                table.style.backgroundColor = backgroundColor;
                KE.attr(table, 'width', '');
                KE.attr(table, 'height', '');
                KE.attr(table, 'bgColor', '');
                KE.attr(table, 'cellPadding', padding);
                KE.attr(table, 'cellSpacing', spacing);
                KE.attr(table, 'align', align);
                if (border === '' || border === '0') {
                    KE.addClass(table, zeroborder);
                } else {
                    KE.removeClass(table, zeroborder);
                }
                KE.attr(table, 'border', border);
                KE.attr(table, 'borderColor', borderColor);
                KE.util.execOnchangeHandler(id);
            } else {
                var style = '';
                if (width !== '') style += 'width:' + width + widthType + ';';
                if (height !== '') style += 'height:' + height + heightType + ';';
                if (backgroundColor !== '') style += 'background-color:' + backgroundColor + ';';
                var html = '<table';
                if (style !== '') html += ' style="' + style + '"';
                if (padding !== '') html += ' cellpadding="' + padding + '"';
                if (spacing !== '') html += ' cellspacing="' + spacing + '"';
                if (align !== '') html += ' align="' + align + '"';
                if (border === '' || border === '0') html += ' class="' + zeroborder + '"';
                if (border !== '') html += ' border="' + border + '"';
                if (borderColor !== '') html += ' bordercolor="' + borderColor + '"';
                html += '>';
                for (var i = 0; i < rows; i++) {
                    html += '<tr>';
                    for (var j = 0; j < cols; j++) {
                        html += '<td>&nbsp;</td>';
                    }
                    html += '</tr>';
                }
                html += '</table>';
                KE.util.insertHtml(id, html);
            }
            this.dialog.hide();
            KE.util.focus(id);
        }
    };

})(KindEditor);
